feat(gerp): 员工列表支持按需统计去重用户数
部门详情「部门员工」与公司详情「员工」卡片标题需要显示「角色数:x/用户数:y」。
get_table 新增 WithUserCount 参数:带该参数时额外统计去重用户数(UserRole 直接按
UserUUID 去重;UserRoleDepartment/UserRoleWorkgroup 只存 UserRoleUUID 且非外键,
需回查 UserRole 后再去重)。默认不统计,避免给所有列表加一次查询。
Get/List 响应新增 _FormListResponse:仅在请求了计数时改为 {list, total, user_count}
对象结构 —— 前端 axios 拦截器只透传 data,计数挂在 data 之外读不到;不带该参数时
仍返回原有的数组结构,既有调用方行为不变。
This commit is contained in:
1 parent
d92797eb31
commit
07e44eefc6
2 files changed
+49
-4
No files matched your search
@@ -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(唯一实现)
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in new issue
Block a user