公共模型 新建文件commonextendsmodels。py,将cmdbmodels。py里定义的TimeAbstract、CommonParent移到这里fromdjango。dbimportmodelsclassTimeAbstract(models。Model):updatetimemodels。DateTimeField(autonowTrue,nullTrue,blankTrue,verbosename更新时间)createdtimemodels。DateTimeField(autonowaddTrue,nullTrue,blankTrue,verbosename创建时间)classExtMeta:relatedFalsedashboardFalseclassMeta:abstractTrueordering〔id〕classCommonParent(models。Model):parentmodels。ForeignKey(self,nullTrue,blankTrue,ondeletemodels。SETNULL,relatednamechildren)classMeta:abstractTrue创建用户模块 新建用户中心模块(venv)ydevopsbackenddjangoadminstartappucenter(venv)ydevopsbackendmvucenterapps 编写用户组织架构及rbac模型fromdjango。dbimportmodelsfromdjango。contrib。auth。modelsimportAbstractUserfromcommon。extends。modelsimportTimeAbstract,CommonParentCreateyourmodelshere。deforgextradata():return{leaderuserid:,存储部门领导IDdn:,存储ldapdn}defuserextradata():return{dinguserid:,钉钉用户IDfeishuuserid:,飞书UserIDfeishuunionid:,飞书UnionIDfeishuopenid:,飞书OpenIDleaderuserid:,直属领导IDdn:,ldapdn}classMenu(TimeAbstract,CommonParent):菜单模型namemodels。CharField(maxlength30,uniqueTrue,verbosename菜单名)titlemodels。CharField(maxlength30,nullTrue,blankTrue,verbosename菜单显示名)iconmodels。CharField(maxlength50,nullTrue,blankTrue,verbosename图标)pathmodels。CharField(maxlength158,nullTrue,blankTrue,verbosename路由地址)redirectmodels。CharField(maxlength200,nullTrue,blankTrue,verbosename跳转地址)isframemodels。BooleanField(defaultFalse,verbosename外部菜单)hiddenmodels。BooleanField(defaultFalse,verbosename是否隐藏)spreadmodels。BooleanField(defaultFalse,verbosename是否默认展开)sortmodels。IntegerField(default0,verbosename排序标记)componentmodels。CharField(maxlength200,defaultLayout,verbosename组件)affixmodels。BooleanField(defaultFalse,verbosename固定标签)singlemodels。BooleanField(defaultFalse,verbosename标签单开)activeMenumodels。CharField(maxlength128,blankTrue,nullTrue,verbosename激活菜单)defstr(self):returnself。nameclassMeta:defaultpermissions()verbosename菜单verbosenamepluralverbosename管理ordering〔sort,name〕classPermission(TimeAbstract,CommonParent):权限模型namemodels。CharField(maxlength30,uniqueTrue,verbosename权限名)methodmodels。CharField(maxlength50,nullTrue,blankTrue,verbosename方法)defstr(self):returnself。nameclassMeta:defaultpermissions()verbosename权限verbosenamepluralverbosename管理classRole(TimeAbstract):角色模型namemodels。CharField(maxlength32,uniqueTrue,verbosename角色)permissionsmodels。ManyToManyField(Permission,blankTrue,relatednamerolepermission,verbosename权限)menusmodels。ManyToManyField(Menu,blankTrue,verbosename菜单)descmodels。CharField(maxlength50,blankTrue,nullTrue,verbosename描述)defstr(self):returnself。nameclassMeta:defaultpermissions()verbosename角色verbosenamepluralverbosename管理classOrganization(TimeAbstract,CommonParent):组织架构organizationtypechoices((company,公司),(department,部门))deptidmodels。CharField(maxlength32,uniqueTrue,verbosename部门ID)namemodels。CharField(maxlength60,verbosename名称)typemodels。CharField(maxlength20,choicesorganizationtypechoices,defaultdepartment,verbosename类型)extradatamodels。JSONField(defaultorgextradata,verbosename其它数据,helptextf数据格式:{orgextradata()})propertydeffull(self):l〔〕self。getparents(l)returnldefgetparents(self,parentresult:list):ifnotparentresult:parentresult。append(self)parentobjself。parentifparentobj:parentresult。append(parentobj)parentobj。getparents(parentresult)defstr(self):returnself。nameclassExtMeta:relatedTruedashboardFalseclassMeta:defaultpermissions()verbosename组织架构verbosenamepluralverbosename管理classUserProfile(TimeAbstract,AbstractUser):用户信息mobilemodels。CharField(maxlength11,nullTrue,blankTrue,verbosename手机号码)avatarmodels。ImageField(uploadtostaticYm,defaultimagedefault。png,maxlength250,nullTrue,blankTrue)departmentmodels。ManyToManyField(Organization,relatednameorguser,verbosename部门)职能:根据职能授权positionmodels。CharField(maxlength50,nullTrue,blankTrue,verbosename职能)职位:仅展示用户title信息titlemodels。CharField(maxlength50,nullTrue,blankTrue,verbosename职位)rolesmodels。ManyToManyField(Role,verbosename角色,relatednameuserrole,blankTrue)extradatamodels。JSONField(defaultuserextradata,verbosename其它数据,helptextf数据格式:{userextradata()})isldapmodels。BooleanField(defaultFalse,verbosename是否ldap用户)propertydefname(self):ifself。firstname:returnself。firstnamereturnself。usernamedefstr(self):returnself。nameclassExtMeta:relatedTruedashboardFalseiconpeoplesclassMeta:defaultpermissions()verbosename用户信息verbosenamepluralverbosename管理ordering〔id〕 安装依赖pipinstallpillow 添加模块到settings。py及配置自定义的用户认证模型INSTALLEDAPPS〔。。。ucenter。apps。UcenterConfig,〕。。。AUTHUSERMODELucenter。UserProfileCMDB模型更新 调整cmdb模块里的模型注释原有用户模型,替换为自定义的模型fromdjango。contrib。auth。modelsimportUserfromucenter。modelsimportUserProfileasUserfrom。modelassetsimportIdc,Region从common里导入TimeAbstract,CommonParentfromcommon。extends。modelsimportTimeAbstract,CommonParent迁移数据(venv)ydevopsbackendpythonmanage。pymakemigrationsMigrationsforucenter:appsucentermigrations0001initial。pyCreatemodelMenuCreatemodelPermissionCreatemodelRoleCreatemodelOrganizationCreatemodelUserProfile 此时创建表时会有异常(venv)ydevopsbackendpythonmanage。pymigrate。。。django。db。migrations。exceptions。InconsistentMigrationHistory:Migrationadmin。0001initialisappliedbeforeitsdependencyucenter。0001initialondatabasedefault。 我们现在开发阶段,最省事的就是直接把database删除,重新生成rmdb。sqlite3pythonmanage。pymakemigrationspythonmanage。pymigrate 当然,如果想尝试解决,我们按如下步骤注释settings。pyINSTAALLLEDAPPS里的django。contrib。admin;注释urls。pyurlpatterns里的path(admin,admin。site。urls)。admin是django自带的管理后台,我们可以不用这个,直接删除也行。。。。。。删除cmdb里的迁移文件appscmdbmigrations0。py创建ucenterpythonmanage。pymigrateucenter重新生成迁移文件pythonmanage。pymakemigrations查看迁移,确保所有文件已执行完成pythonmanage。pyshowmigrations 由于我们使用了自定义的用户模型,需要重新创建用户(venv)ydevopsbackendpythonmanage。pycreatesuperuser编写序列化器classUserProfileListSerializers(serializers。ModelSerializer):userdepartmentserializers。SerializerMethodField()userdirectorserializers。SerializerMethodField()defgetuserdepartment(self,instance):return〔{orgid:i。id,orgname:i。name}foriininstance。department。all()〕defgetuserdirector(self,instance):leaderou〔i。extradata〔leaderuserid〕foriininstance。department。all()ifi。extradata。get(leaderuserid,None)〕leadersUserProfile。objects。filter(extradatafeishuopenidinleaderou)return〔〔{id:i。id,name:i。name}foriinleaders〕〕classMeta:modelUserProfileexclude(password,dn)classUserProfileDetailSerializers(UserProfileListSerializers):userrolesserializers。SerializerMethodField()routersserializers。SerializerMethodField()permissionsserializers。SerializerMethodField()defgetuserroles(self,instance):try:qsinstance。roles。all()return〔{id:i。id,name:i。name,desc:i。desc}foriinqs〕exceptBaseExceptionase:return〔〕defgetpermissions(self,instance):permsinstance。roles。values(permissionsmethod,)。distinct()ifinstance。issuperuser:return〔admin〕return〔p〔permissionsmethod〕forpinpermsifp〔permissionsmethod〕〕defgetrouters(self,instance):qs〔〕ifinstance。issuperuseroradminin〔p〔permissionsmethod〕forpininstance。roles。values(permissionsmethod)〕:qsMenu。objects。filter(parentisnullTrue)serializerMenuListSerializers(instanceqs,manyTrue)treedataserializer。dataelse:〔qs。extend(i。menus。all())foriininstance。roles。all()〕serializerUserMenuSerializers(instanceqs,manyTrue)组织用户拥有的菜单列表treedict{}treedata〔〕try:foriteminserializer。data:treedict〔item〔id〕〕itemforiintreedict:iftreedict〔i〕〔parent〕:pidtreedict〔i〕〔parent〕parenttreedict〔pid〕parent。setdefault(children,〔〕)。append(treedict〔i〕)else:treedata。append(treedict〔i〕)except:treedataserializer。datareturntreedataclassMeta:modelUserProfileexclude(avatar,)classUserProfileSerializers(serializers。ModelSerializer):classMeta:modelUserProfileexclude(avatar,)defcreate(self,validateddata):rolesvalidateddata。pop(roles)departmentsvalidateddata。pop(department)instanceUserProfile。objects。create(validateddata)instance。setpassword(validateddata〔password〕)instance。save()instance。department。set(departments)instance。roles。set(roles)returninstance用户管理视图importshortuuidfromdjango。db。modelsimportQfromdjango。core。cacheimportcachefromconfigimportUSERAUTHBACKENDimportloggingloggerlogging。getLogger(name)USERSYNCKEY{feishu:celeryjob:feishuusersync,同步飞书组织架构任务keyldap:celeryjob:ldapusersync,LDAP用户同步任务KEY}classUserViewSet(AutoModelViewSet):用户管理视图用户管理权限{:(userall,用户管理)},{get:(userlist,查看用户)},{post:(usercreate,创建用户)},{put:(useredit,编辑用户)},{patch:(useredit,编辑用户)},{delete:(userdelete,删除用户)}permsmap({:(admin,管理员)},{:(userall,用户管理)},{get:(userlist,查看用户)},{post:(usercreate,创建用户)},{put:(useredit,编辑用户)},{patch:(useredit,编辑用户)},{delete:(userdelete,删除用户)})querysetUserProfile。objects。exclude(Q(usernamethirdparty)Q(isactiveFalse))serializerclassUserProfileSerializersserializerlistclassUserProfileListSerializersdefgetserializerclass(self):ifself。actionin〔detail,retrieve〕:returnUserProfileDetailSerializersreturnsuper()。getserializerclass()defcreate(self,request,args,kwargs):ifself。queryset。filter(usernamerequest。data〔username〕):returnopsresponse({},successFalse,errorCode40300,errorMessages账号已存在!request。data〔username〕)passwordshortuuid。ShortUUID()。random(length8)request。data〔password〕passwordserializerself。getserializer(datarequest。data)serializer。isvalid(raiseexceptionTrue)self。performcreate(serializer)dataserializer。datadata〔password〕passworddata〔status〕successdata〔code〕20000returnopsresponse(data)defperformdestroy(self,instance):禁用用户instance。isactiveFalseinstance。save()action(methods〔POST〕,urlpathpasswordreset,detailFalse)defpasswordreset(self,request):重置用户密码重置用户密码dataself。request。datauserself。queryset。get(pkdata〔uid〕)ifuser。issuperuser:returnopsresponse({},successFalse,errorCode40300,errorMessage禁止修改管理员密码!)user。setpassword(data〔password〕)user。save()returnopsresponse(密码已更新。)action(methods〔GET〕,urlpathdetail,detailFalse)defdetailinfo(self,request,pkNone,args,kwargs):用户详细列表获取用户详细信息,用户管理模块returnsuper()。list(request,pk,args,kwargs)action(methods〔POST〕,urlpathsync,detailFalse)defusersync(self,request):用户同步传递参数:sync:1syncrequest。data。get(sync,0)isjobexistcache。get(USERSYNCKEY〔USERAUTHBACKEND〕)ifisjobexist:returnopsresponse({},successFalse,errorCode40300,errorMessage已经有组织架构同步任务在运行中。。。请稍后刷新页面查看)ifsync:同步任务,后面再实现taskidNone限制只能有一个同步任务在跑cache。set(USERSYNCKEY〔USERAUTHBACKEND〕,taskid,timeout300)returnopsresponse(正在同步组织架构信息。。。)添加路由 加用户管理的路由加到devopsbackendurls。py。。。router。register(users,UserViewSet)运行项目 访问http:localhost:9000apidoc,输入users过滤可以看到用户模块接口