@@ -189,9 +189,82 @@ func (d *UserRepo) ListUsers(ctx context.Context, opt *biz.ListUsersOption) (use
189189 }
190190 users = append (users , ds )
191191 }
192+
193+ // Batch fill projects = direct members ∪ member groups (same semantics as GetUserProject; no N+1).
194+ uids := make ([]string , 0 , len (users ))
195+ for _ , u := range users {
196+ if u != nil && u .UID != "" {
197+ uids = append (uids , u .UID )
198+ }
199+ }
200+ projectNamesByUID , err := d .listProjectNamesByUserUIDs (ctx , uids )
201+ if err != nil {
202+ return nil , 0 , pkgErr .WrapStorageErr (d .log , err )
203+ }
204+ for _ , u := range users {
205+ if u == nil {
206+ continue
207+ }
208+ if names , ok := projectNamesByUID [u .UID ]; ok {
209+ u .Projects = names
210+ } else {
211+ u .Projects = []string {}
212+ }
213+ }
192214 return users , total , nil
193215}
194216
217+ type userProjectNameRow struct {
218+ UserUID string `gorm:"column:user_uid"`
219+ ProjectName string `gorm:"column:project_name"`
220+ }
221+
222+ // listProjectNamesByUserUIDs returns project names per user UID in one query
223+ // (members UNION member_groups via member_group_users), matching GetUserProject set semantics.
224+ func (d * UserRepo ) listProjectNamesByUserUIDs (ctx context.Context , uids []string ) (map [string ][]string , error ) {
225+ result := make (map [string ][]string )
226+ if len (uids ) == 0 {
227+ return result , nil
228+ }
229+
230+ var rows []userProjectNameRow
231+ if err := transaction (d .log , ctx , d .db , func (tx * gorm.DB ) error {
232+ if err := tx .WithContext (ctx ).Raw (`
233+ SELECT m.user_uid AS user_uid, n.name AS project_name
234+ FROM projects n
235+ JOIN members m ON n.uid = m.project_uid
236+ WHERE m.user_uid IN (?)
237+ UNION
238+ SELECT mgu.user_uid AS user_uid, n.name AS project_name
239+ FROM projects n
240+ JOIN member_groups mg ON n.uid = mg.project_uid
241+ JOIN member_group_users mgu ON mgu.member_group_uid = mg.uid
242+ WHERE mgu.user_uid IN (?)
243+ ` , uids , uids ).Scan (& rows ).Error ; err != nil {
244+ return fmt .Errorf ("failed to list project names by user uids: %v" , err )
245+ }
246+ return nil
247+ }); err != nil {
248+ return nil , err
249+ }
250+
251+ seen := make (map [string ]map [string ]struct {}, len (uids ))
252+ for _ , row := range rows {
253+ if row .UserUID == "" || row .ProjectName == "" {
254+ continue
255+ }
256+ if _ , ok := seen [row .UserUID ]; ! ok {
257+ seen [row .UserUID ] = make (map [string ]struct {})
258+ }
259+ if _ , dup := seen [row.UserUID ][row.ProjectName ]; dup {
260+ continue
261+ }
262+ seen [row.UserUID ][row.ProjectName ] = struct {}{}
263+ result [row .UserUID ] = append (result [row .UserUID ], row .ProjectName )
264+ }
265+ return result , nil
266+ }
267+
195268func (d * UserRepo ) CountUsers (ctx context.Context , opts []constant.FilterCondition ) (total int64 , err error ) {
196269 if err := transaction (d .log , ctx , d .db , func (tx * gorm.DB ) error {
197270 // find total
0 commit comments