diff --git a/gvsds/apps/gerp/form_service.py b/gvsds/apps/gerp/form_service.py index e0110d55..9506687d 100644 --- a/gvsds/apps/gerp/form_service.py +++ b/gvsds/apps/gerp/form_service.py @@ -1349,6 +1349,25 @@ class FormService(FormServiceBase): return 'select' return mapping.get(t, 'text') + def CountDistinctUsers(self, table_name, qs): + """统计 qs 命中的**去重用户数**(角色数 = qs.count(),由调用方给出) + + 为什么要走不同路径: + - UserRole 自身带 UserUUID 列,直接去重计数即可; + - UserRoleDepartment / UserRoleWorkgroup 只存 UserRoleUUID,而且该字段是普通 + UUIDField 而非 ForeignKey,不能用 `__` 跨表,必须拿 UUID 列表回查 UserRole + 再按 UserUUID 去重(与 get_table 里为这两张表补 UserUUID 的做法一致)。 + 其余表没有"用户"语义,返回 None(调用方据此退回默认响应结构)。 + """ + if table_name == 'UserRole': + return qs.values('UserUUID').distinct().count() + if table_name in ('UserRoleDepartment', 'UserRoleWorkgroup'): + from gvsdsdk.models import UserRole + return UserRole.objects.filter( + UserRoleUUID__in=qs.values_list('UserRoleUUID', flat=True) + ).values('UserUUID').distinct().count() + return None + def get_table(self, table_name, params): model = self.TABLE_MAP.get(table_name) if not model: @@ -1437,8 +1456,14 @@ class FormService(FormServiceBase): qs = self.ApplyExactFilters(qs, model, params, SkipKeys=_SKIP_FILTER_PARAMS) qs = self.ApplyKeyword(qs, model, params.get('keyword', '')) page, page_size = self.ParsePagination(params) + # 员工卡片标题「角色数/用户数」:角色数就是关系条数(total),用户数是去重用户数。 + # 默认不算(多一次统计查询),调用方显式带 WithUserCount=1 时才计算。 + # 先置 None 是为了下面的 except 分支也能正常收尾(该分支会重建 total/records) + user_count = None try: total = qs.count() + if params.get('WithUserCount'): + user_count = self.CountDistinctUsers(table_name, qs) start = (page - 1) * page_size # 识别 entityPicker 字段,使用 select_related 预加载关联实体 # 注意:select_related 只能用于 ForeignKey,UUIDField 类型的 entityPicker 不能预加载 @@ -1773,7 +1798,7 @@ class FormService(FormServiceBase): logging.error('[FormService] get_table error: table=%s, error=%s', table_name, e, exc_info=True) total = 0 records = [] - return {'records': records, 'total': total, 'page': page, 'PageSize': page_size} + return {'records': records, 'total': total, 'page': page, 'PageSize': page_size, 'user_count': user_count} def _ResolveDeptEmployee(self, data): """解析「部门员工关联」选择器提交的 UserRoleDepartmentUUID(唯一实现) diff --git a/gvsds/apps/gerp/views/form_api.py b/gvsds/apps/gerp/views/form_api.py index 0ec3e348..e6aced8e 100644 --- a/gvsds/apps/gerp/views/form_api.py +++ b/gvsds/apps/gerp/views/form_api.py @@ -171,6 +171,26 @@ def _check_table_access(table_name, action, request): return None +def _FormListResponse(result): + """Get/List 的统一响应体 + + 默认沿用历史结构(data=记录数组、total 平铺),不动既有调用方的解析; + 仅当请求带 WithUserCount(员工卡片「角色数:x/用户数:y」)时改为对象结构 —— + 前端 axios 响应拦截器只透传 `data`,计数这类元数据挂在 data 之外是读不到的。 + """ + if result.get('user_count') is None: + return {'code': 0, 'data': result['records'], 'total': result['total']} + return { + 'code': 0, + 'data': { + 'list': result['records'], + 'total': result['total'], + 'user_count': result['user_count'], + }, + 'total': result['total'], + } + + class FormAPICompatView(ELTAuthenticatedAPIView): def patch(self, request, **kwargs): return self.post(request, **kwargs) @@ -198,7 +218,7 @@ class FormAPICompatView(ELTAuthenticatedAPIView): if access_error: return access_error result = service.get_table(table_name, request.query_params) - return Response({'code': 0, 'data': result['records'], 'total': result['total']}) + return Response(_FormListResponse(result)) elif '/CodeDisplay' in path: result = service.code_display(request.query_params.dict()) return Response({'code': 0, 'data': result}) @@ -210,7 +230,7 @@ class FormAPICompatView(ELTAuthenticatedAPIView): if access_error: return access_error result = service.get_table(table_name, request.query_params) - return Response({'code': 0, 'data': result['records'], 'total': result['total']}) + return Response(_FormListResponse(result)) def post(self, request, **kwargs): user = request.user if hasattr(request, 'user') and request.user.is_authenticated else None @@ -354,7 +374,7 @@ class FormAPIView(ELTAuthenticatedAPIView): return access_error service = _make_service(request, request.user) result = service.get_table(table_name, request.query_params) - return Response({'code': 0, 'data': result['records'], 'total': result['total']}) + return Response(_FormListResponse(result)) def post(self, request): action = request.data.get('Action', 'add')