@@ -86,80 +86,202 @@ def build_user_infos(self, tenant_users: QuerySet[TenantUser], fields: List[str]
8686 且必须保证 select_related("data_source_user")
8787 :param fields: 对外的用户字段列表,空时表示所有用户字段都对外
8888 """
89- # 按需提前获取用户 Leader 信息 和 用户部门信息
89+ # 标准化字段列表
90+ fields = self ._get_default_fields (fields )
91+
92+ # 优化查询:只查询需要的数据库字段
93+ tenant_users = self ._optimize_queryset (tenant_users , fields )
94+
95+ # 批量预加载关联数据
96+ context = self ._prepare_context (tenant_users , fields )
97+
98+ # 构建每个用户的信息
99+ return [self ._build_single_user_info (tenant_user , fields , context ) for tenant_user in tenant_users ]
100+
101+ @staticmethod
102+ def _get_default_fields (fields : List [str ]) -> List [str ]:
103+ """获取默认字段列表"""
104+ return fields or [
105+ "id" ,
106+ "username" ,
107+ "display_name" ,
108+ "email" ,
109+ "telephone" ,
110+ "country_code" ,
111+ "time_zone" ,
112+ "language" ,
113+ "wx_userid" ,
114+ "domain" ,
115+ "category_id" ,
116+ "status" ,
117+ "extras" ,
118+ "position" ,
119+ "leader" ,
120+ "departments" ,
121+ ]
122+
123+ @staticmethod
124+ def _get_db_field_map () -> Dict [str , List [str ]]:
125+ """获取字段到数据库字段的映射"""
126+ return {
127+ "id" : ["data_source_user__id" ],
128+ "username" : ["id" ],
129+ "display_name" : ["data_source_user__full_name" ],
130+ "email" : ["custom_email" , "is_inherited_email" , "data_source_user__email" ],
131+ "telephone" : ["custom_phone" , "is_inherited_phone" , "data_source_user__phone" ],
132+ "country_code" : [
133+ "custom_phone_country_code" ,
134+ "is_inherited_phone" ,
135+ "data_source_user__phone_country_code" ,
136+ ],
137+ "time_zone" : ["time_zone" ],
138+ "language" : ["language" ],
139+ "wx_userid" : ["wx_userid" ],
140+ "domain" : ["data_source_id" , "tenant_id" ],
141+ "category_id" : ["data_source_id" ],
142+ "status" : ["status" ],
143+ "extras" : ["data_source_user__extras" ],
144+ "position" : ["data_source_user__extras" ],
145+ "leader" : ["data_source_user__id" ],
146+ "departments" : ["data_source_user__id" ],
147+ }
148+
149+ def _optimize_queryset (self , tenant_users : QuerySet [TenantUser ], fields : List [str ]) -> QuerySet [TenantUser ]:
150+ """优化查询,只查询需要的数据库字段"""
151+ db_field_map = self ._get_db_field_map ()
152+ only_fields = {"id" }
153+ for f in fields :
154+ only_fields .update (db_field_map .get (f , []))
155+ return tenant_users .only (* only_fields )
156+
157+ def _prepare_context (self , tenant_users : QuerySet [TenantUser ], fields : List [str ]) -> Dict [str , Any ]:
158+ """预加载关联数据,减少数据库查询"""
90159 data_source_user_ids = [i .data_source_user .id for i in tenant_users ]
91- leader_map = self ._get_leader_map (data_source_user_ids ) if not fields or "leader" in fields else {}
92- department_map = (
93- self ._get_department_map (data_source_user_ids ) if not fields or "departments" in fields else {}
94- )
95160
96- collaboration_field_mapping = self .get_collaboration_field_mapping ()
97-
98- user_infos = []
99- for tenant_user in tenant_users :
100- # 手机号和手机区号
101- phone , phone_country_code = tenant_user .phone_info
102-
103- # 自定义字段
104- source_extras = tenant_user .data_source_user .extras
105- # 协同时按照协同租户配置的用户自定义字段进行输出
106- ds_owner_tenant_id = tenant_user .data_source .owner_tenant_id
107- if ds_owner_tenant_id != tenant_user .tenant_id :
108- extras = {
109- collaboration_field_mapping [(ds_owner_tenant_id , k )]: v
110- for k , v in source_extras .items ()
111- if (ds_owner_tenant_id , k ) in collaboration_field_mapping
112- }
113- else :
114- extras = source_extras
115-
116- # 不会放大查询的字段
117- user_info = {
118- "id" : tenant_user .data_source_user .id ,
119- # 租户用户 ID 即为对外的 username / bk_username
120- "username" : tenant_user .id ,
121- "display_name" : tenant_user .data_source_user .full_name ,
122- "email" : tenant_user .email ,
123- "telephone" : phone ,
124- "country_code" : phone_country_code ,
125- "iso_code" : _phone_country_code_to_iso_code (phone_country_code ),
126- "time_zone" : tenant_user .time_zone ,
127- "language" : tenant_user .language ,
128- "wx_userid" : tenant_user .wx_userid ,
129- "domain" : self .get_domain (tenant_user .data_source_id , tenant_user .tenant_id ),
130- "category_id" : tenant_user .data_source_id ,
131- "status" : TENANT_USER_STATUS_TO_PROFILE_STATUS_MAP .get (tenant_user .status , tenant_user .status ),
132- "enabled" : True ,
133- "extras" : extras ,
134- # 旧版本内置字段,新版本迁移在自定义字段里
135- "position" : int (source_extras .get ("position" , 0 )),
136- # 总是返回固定值
137- "logo" : "" ,
138- "staff_status" : "IN" ,
139- }
161+ return {
162+ "leader_map" : self ._get_leader_map (data_source_user_ids ) if "leader" in fields else {},
163+ "department_map" : self ._get_department_map (data_source_user_ids ) if "departments" in fields else {},
164+ "collaboration_field_mapping" : (
165+ self .get_collaboration_field_mapping () if "extras" in fields or "position" in fields else {}
166+ ),
167+ }
140168
141- # 指定对外字段,则只返回指定的字段
142- if fields :
143- user_info = {k : v for k , v in user_info .items () if k in fields }
144- # 由于 leader 需要额外计算,因此特殊分支处理
145- if "leader" in fields :
146- user_info ["leader" ] = leader_map .get ((tenant_user .tenant .id , tenant_user .data_source_user .id ))
147- # 由于 department 需要额外计算,因此特殊分支处理
148- if "departments" in fields :
149- user_info ["departments" ] = department_map .get (
150- (tenant_user .tenant .id , tenant_user .data_source_user .id )
151- )
169+ def _build_single_user_info (
170+ self , tenant_user : TenantUser , fields : List [str ], context : Dict [str , Any ]
171+ ) -> Dict [str , Any ]:
172+ """构建单个用户的信息"""
173+ user_info : Dict [str , Any ] = {}
152174
153- user_infos . append ( user_info )
154- continue
175+ # 添加基础字段
176+ self . _add_basic_fields ( user_info , tenant_user , fields )
155177
156- # 未指定字段,则关联字段也要返回
157- user_info ["leader" ] = leader_map .get ((tenant_user .tenant .id , tenant_user .data_source_user .id ))
158- user_info ["departments" ] = department_map .get ((tenant_user .tenant .id , tenant_user .data_source_user .id ))
178+ # 添加电话相关字段
179+ self ._add_phone_fields (user_info , tenant_user , fields )
159180
160- user_infos .append (user_info )
181+ # 添加简单字段
182+ self ._add_simple_fields (user_info , tenant_user , fields )
183+
184+ # 添加自定义字段
185+ self ._add_extras_fields (user_info , tenant_user , fields , context ["collaboration_field_mapping" ])
186+
187+ # 添加固定值字段
188+ user_info ["logo" ] = ""
189+ user_info ["staff_status" ] = "IN"
190+
191+ # 添加关联字段
192+ self ._add_relation_fields (user_info , tenant_user , fields , context )
193+
194+ return user_info
161195
162- return user_infos
196+ @staticmethod
197+ def _add_basic_fields (user_info : Dict [str , Any ], tenant_user : TenantUser , fields : List [str ]) -> None :
198+ """添加基础字段"""
199+ if "id" in fields :
200+ user_info ["id" ] = tenant_user .data_source_user .id
201+ if "username" in fields :
202+ user_info ["username" ] = tenant_user .id
203+ if "display_name" in fields :
204+ user_info ["display_name" ] = tenant_user .data_source_user .full_name
205+ if "email" in fields :
206+ user_info ["email" ] = tenant_user .email
207+
208+ @staticmethod
209+ def _add_phone_fields (user_info : Dict [str , Any ], tenant_user : TenantUser , fields : List [str ]) -> None :
210+ """添加电话相关字段"""
211+ if not any (f in fields for f in ["telephone" , "country_code" , "iso_code" ]):
212+ return
213+
214+ phone , phone_country_code = tenant_user .phone_info
215+ if "telephone" in fields :
216+ user_info ["telephone" ] = phone
217+ if "country_code" in fields :
218+ user_info ["country_code" ] = phone_country_code
219+ if "iso_code" in fields :
220+ user_info ["iso_code" ] = _phone_country_code_to_iso_code (phone_country_code )
221+
222+ def _add_simple_fields (self , user_info : Dict [str , Any ], tenant_user : TenantUser , fields : List [str ]) -> None :
223+ """添加简单字段"""
224+ simple_field_mapping = {
225+ "time_zone" : lambda : tenant_user .time_zone ,
226+ "language" : lambda : tenant_user .language ,
227+ "wx_userid" : lambda : tenant_user .wx_userid ,
228+ "domain" : lambda : self .get_domain (tenant_user .data_source_id , tenant_user .tenant_id ),
229+ "category_id" : lambda : tenant_user .data_source_id ,
230+ "status" : lambda : TENANT_USER_STATUS_TO_PROFILE_STATUS_MAP .get (tenant_user .status , tenant_user .status ),
231+ }
232+
233+ for field_name , value_func in simple_field_mapping .items ():
234+ if field_name in fields :
235+ user_info [field_name ] = value_func ()
236+
237+ @staticmethod
238+ def _add_extras_fields (
239+ user_info : Dict [str , Any ],
240+ tenant_user : TenantUser ,
241+ fields : List [str ],
242+ collaboration_field_mapping : Dict [Tuple [str , str ], str ],
243+ ) -> None :
244+ """添加自定义字段"""
245+ if not ("extras" in fields or "position" in fields ):
246+ return
247+
248+ source_extras = tenant_user .data_source_user .extras
249+ extras = TenantUserListToUserInfosMixin ._get_collaboration_extras (
250+ source_extras , tenant_user , collaboration_field_mapping
251+ )
252+
253+ if "extras" in fields :
254+ user_info ["extras" ] = extras
255+ if "position" in fields :
256+ user_info ["position" ] = int (source_extras .get ("position" , 0 ))
257+
258+ @staticmethod
259+ def _get_collaboration_extras (
260+ source_extras : Dict [str , Any ],
261+ tenant_user : TenantUser ,
262+ collaboration_field_mapping : Dict [Tuple [str , str ], str ],
263+ ) -> Dict [str , Any ]:
264+ """获取协同场景下的自定义字段"""
265+ ds_owner_tenant_id = tenant_user .data_source .owner_tenant_id
266+ if ds_owner_tenant_id != tenant_user .tenant_id :
267+ return {
268+ collaboration_field_mapping [(ds_owner_tenant_id , k )]: v
269+ for k , v in source_extras .items ()
270+ if (ds_owner_tenant_id , k ) in collaboration_field_mapping
271+ }
272+ return source_extras
273+
274+ @staticmethod
275+ def _add_relation_fields (
276+ user_info : Dict [str , Any ], tenant_user : TenantUser , fields : List [str ], context : Dict [str , Any ]
277+ ) -> None :
278+ """添加关联字段"""
279+ if "leader" in fields :
280+ user_info ["leader" ] = context ["leader_map" ].get ((tenant_user .tenant .id , tenant_user .data_source_user .id ))
281+ if "departments" in fields :
282+ user_info ["departments" ] = context ["department_map" ].get (
283+ (tenant_user .tenant .id , tenant_user .data_source_user .id )
284+ )
163285
164286 @staticmethod
165287 def _get_leader_map (data_source_user_ids : List [int ]) -> Dict [Tuple [str , int ], List [Dict [str , Any ]]]:
@@ -480,7 +602,7 @@ def get(self, request, *args, **kwargs):
480602 slz .is_valid (raise_exception = True )
481603 params = slz .validated_data
482604
483- lookup_filter = {}
605+ lookup_filter : Dict [ str , Any ] = {}
484606 if params ["lookup_field" ] == "username" :
485607 # username 其实就是新的租户用户 ID,形式如 admin / admin@qq.com / uuid4 / nanoid
486608 lookup_filter ["id" ] = kwargs ["lookup_value" ]
0 commit comments