feat(app): 公司级+租户级双粒度应用安装——CompanyApp 表、Install/Uninstall 支持指定公司、可管理公司范围校验(super/tenant/company)、CheckAccess 公司维度

This commit is contained in:
TermiNexus committed 2026-08-13 06:28:28 +08:00
1 parent 904e225b90
commit 4de301aa78
9 files changed
+376 -29

No files matched your search

+2 -2
View File
@@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GERP 管理系统</title>
<script type="module" crossorigin src="/assets/index-84d1wZOe.js"></script>
<script type="module" crossorigin src="/assets/index-DxQCUaG4.js"></script>
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
<link rel="modulepreload" crossorigin href="/assets/element-plus-DtMR4CIi.js">
<link rel="modulepreload" crossorigin href="/assets/utils-C0vl14oO.js">
@@ -14,7 +14,7 @@
<link rel="modulepreload" crossorigin href="/assets/formkit-DL7Nej8X.js">
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-DmO0PmrK.js">
<link rel="modulepreload" crossorigin href="/assets/serverScope-C6yhSV7-.js">
<link rel="stylesheet" crossorigin href="/assets/style-VRk9FrhH.css">
<link rel="stylesheet" crossorigin href="/assets/style-Xc5aXuRA.css">
</head>
<body>
<div id="app"></div>
+10 -1
View File
@@ -37,7 +37,16 @@ def AppAccessPermission(AppCode):
user_uuid = getattr(user, 'UserUUID', None)
if not tenant_uuid or not user_uuid:
return False
company_uuid = None
company = getattr(request, 'current_company_uuid', None)
if company is not None:
if hasattr(company, 'bytes'):
company_uuid = company.bytes
elif hasattr(company, 'pk'):
company_uuid = company.pk
from .app_manager import AppManager
return AppManager.CheckAccess(tenant_uuid, user_uuid, AppCode) is not None
return AppManager.CheckAccess(
tenant_uuid, user_uuid, AppCode, CompanyUUID=company_uuid,
) is not None
return _AppAccessPermission
+177 -12
View File
@@ -19,7 +19,7 @@ import uuid
from gvsdsdk.model_utils import UUIDObj
from .models import App, TenantApp, UserApp
from .models import App, TenantApp, CompanyApp, UserApp
# 投放形态
TYPE_ENTERPRISE = 'enterprise'
@@ -54,7 +54,7 @@ def _ToBytes(value):
return value
def _AppFlat(app, tenant_installed=None, user_record=None):
def _AppFlat(app, tenant_installed=None, user_record=None, company_installed=None):
"""App 实例 → 市场/我的应用视图 dict"""
return {
'AppUUID': UUIDObj(app.AppUUID).str,
@@ -69,6 +69,8 @@ def _AppFlat(app, tenant_installed=None, user_record=None):
# 状态附加(市场页/我的应用展示用)
'TenantInstalled': 1 if tenant_installed and tenant_installed.InstallStatus == 1 else 0,
'TenantAuth': tenant_installed.AuthStatus if tenant_installed else 0,
# 公司维度安装(指定公司安装时 TenantInstalled=0、CompanyInstalled=1)
'CompanyInstalled': 1 if company_installed and company_installed.InstallStatus == 1 else 0,
'UserEnabled': 1 if user_record and user_record.UseStatus == 1 else 0,
'ApplyStatus': user_record.ApplyStatus if user_record else 0,
}
@@ -94,12 +96,13 @@ class AppManager:
return App.objects.filter(AppUUID=_ToBytes(AppUUID)).first()
@staticmethod
def ListMarket(TenantUUID=None, UserUUID=None):
"""应用市场列表——每个应用附带租户安装状态与个人启用/申请状态
def ListMarket(TenantUUID=None, UserUUID=None, CompanyUUID=None):
"""应用市场列表——每个应用附带租户/公司安装状态与个人启用/申请状态
Args:
TenantUUID: 当前租户 UUID(bytes/str/UUIDObj),用于标注"企业功能已安装"
UserUUID: 当前用户 UUID,用于标注"个人已启用/申请中"
CompanyUUID: 当前公司 UUID(可选),用于标注"指定公司已安装"
Returns:
list[dict]
@@ -107,10 +110,15 @@ class AppManager:
apps = AppManager.ListEnabledApps()
tenant_bytes = _ToBytes(TenantUUID)
user_bytes = _ToBytes(UserUUID)
company_bytes = _ToBytes(CompanyUUID)
tenant_map = {}
if tenant_bytes:
for ta in TenantApp.objects.filter(TenantUUID=tenant_bytes):
tenant_map[UUIDObj(ta.AppUUID).str] = ta
company_map = {}
if company_bytes:
for ca in CompanyApp.objects.filter(CompanyUUID=company_bytes):
company_map[UUIDObj(ca.AppUUID).str] = ca
user_map = {}
if user_bytes:
for ua in UserApp.objects.filter(UserUUID=user_bytes):
@@ -122,12 +130,13 @@ class AppManager:
app,
tenant_installed=tenant_map.get(key),
user_record=user_map.get(key),
company_installed=company_map.get(key),
))
return result
@staticmethod
def ListMyApps(TenantUUID, UserUUID):
"""我的应用——当前用户可用应用(租户已安装的企业功能 + 个人已启用的应用)
def ListMyApps(TenantUUID, UserUUID, CompanyUUID=None):
"""我的应用——当前用户可用应用(租户已装或当前公司已装的企业功能 + 个人已启用的应用)
需企业授权(AuthRequired=1)但个人未获授权的应用不出现在可用列表,
仅在企业功能已安装时对全员生效(企业授权在企业层面开通)。
@@ -135,10 +144,15 @@ class AppManager:
apps = AppManager.ListEnabledApps()
tenant_bytes = _ToBytes(TenantUUID)
user_bytes = _ToBytes(UserUUID)
company_bytes = _ToBytes(CompanyUUID)
tenant_map = {}
if tenant_bytes:
for ta in TenantApp.objects.filter(TenantUUID=tenant_bytes, InstallStatus=1):
tenant_map[UUIDObj(ta.AppUUID).str] = ta
company_map = {}
if company_bytes:
for ca in CompanyApp.objects.filter(CompanyUUID=company_bytes, InstallStatus=1):
company_map[UUIDObj(ca.AppUUID).str] = ca
user_map = {}
if user_bytes:
for ua in UserApp.objects.filter(UserUUID=user_bytes, UseStatus=1):
@@ -147,10 +161,11 @@ class AppManager:
for app in apps:
key = UUIDObj(app.AppUUID).str
tenant_installed = tenant_map.get(key)
company_installed = company_map.get(key)
user_record = user_map.get(key)
if app.AppType == TYPE_ENTERPRISE:
# 企业功能:租户已安装即可用(企业授权在企业层面开通)
if not tenant_installed:
# 企业功能:租户已安装 或 当前公司已安装 即可用
if not tenant_installed and not company_installed:
continue
elif app.AppType == TYPE_PERSONAL:
# 个人应用:个人已启用可用
@@ -158,9 +173,14 @@ class AppManager:
continue
else: # both
# 混合:企业已安装 或 个人已启用
if not tenant_installed and not user_record:
if not tenant_installed and not company_installed and not user_record:
continue
result.append(_AppFlat(app, tenant_installed=tenant_installed, user_record=user_record))
result.append(_AppFlat(
app,
tenant_installed=tenant_installed,
user_record=user_record,
company_installed=company_installed,
))
return result
# ==================== 企业安装/停用 ====================
@@ -206,6 +226,87 @@ class AppManager:
ta.save(update_fields=['InstallStatus'])
return True, ''
# ==================== 公司维度安装/停用 ====================
@staticmethod
def InstallCompanyApps(CompanyUUIDs, AppUUID, InstallBy=None):
"""为指定一个或多个公司安装企业功能(enterprise/both)。
Args:
CompanyUUIDs: 公司 UUID 列表(str/bytes/UUIDObj 均可,逐个写入 CompanyApp)
AppUUID: 应用 UUID
InstallBy: 安装人 UUID bytes(可选)
Returns:
(ok, err, installed_count)
"""
app = AppManager.GetApp(AppUUID)
if app is None or app.AppStatus != 1:
return False, '应用不存在或已禁用', 0
if app.AppType == TYPE_PERSONAL:
return False, '该应用为个人应用,不支持企业安装', 0
company_list = [_ToBytes(c) for c in (CompanyUUIDs or []) if _ToBytes(c)]
if not company_list:
return False, '未指定要安装的公司', 0
install_by = _ToBytes(InstallBy)
for company_bytes in company_list:
ca, created = CompanyApp.objects.get_or_create(
CompanyUUID=company_bytes,
AppUUID=app.AppUUID,
defaults={
'CompanyAppUUID': uuid.uuid4().bytes,
'InstallStatus': 1,
'InstallTime': datetime.datetime.utcnow(),
'InstallBy': install_by,
},
)
if not created:
ca.InstallStatus = 1
if install_by:
ca.InstallBy = install_by
ca.save(update_fields=['InstallStatus', 'InstallBy'])
return True, '', len(company_list)
@staticmethod
def UninstallCompanyApps(CompanyUUIDs, AppUUID):
"""为指定一个或多个公司停用企业功能"""
company_list = [_ToBytes(c) for c in (CompanyUUIDs or []) if _ToBytes(c)]
if not company_list:
return False, '未指定要停用的公司'
app_bytes = _ToBytes(AppUUID)
updated = 0
for company_bytes in company_list:
ca = CompanyApp.objects.filter(
CompanyUUID=company_bytes, AppUUID=app_bytes,
).first()
if ca is None:
continue
ca.InstallStatus = 0
ca.save(update_fields=['InstallStatus'])
updated += 1
if updated == 0:
return False, '所选公司均未安装该功能'
return True, ''
@staticmethod
def ListCompanyInstalledApps(CompanyUUID):
"""列出指定公司已安装的应用编码集合(含停用记录)"""
company_bytes = _ToBytes(CompanyUUID)
if not company_bytes:
return {}
result = {}
for ca in CompanyApp.objects.filter(CompanyUUID=company_bytes):
app = AppManager.GetApp(ca.AppUUID)
if app is None:
continue
result[app.AppCode] = {
'AppUUID': UUIDObj(app.AppUUID).str,
'AppCode': app.AppCode,
'AppName': app.AppName,
'InstallStatus': ca.InstallStatus,
}
return result
# ==================== 个人启用/申请/审批 ====================
@staticmethod
@@ -314,9 +415,19 @@ class AppManager:
# ==================== 访问校验(菜单注入用) ====================
@staticmethod
def CheckAccess(TenantUUID, UserUUID, AppCode):
def CheckAccess(TenantUUID, UserUUID, AppCode, CompanyUUID=None):
"""校验用户当前是否可用某应用(供菜单注入/接口鉴权)。
企业功能可用判定(enterprise/both):
- 租户已安装(TenantApp) → 可用
- 当前公司已安装(CompanyApp,传 CompanyUUID 时)→ 可用
Args:
TenantUUID: 当前租户 UUID
UserUUID: 当前用户 UUID
AppCode: 应用编码
CompanyUUID: 当前公司 UUID(可选,用于公司维度安装判定)
Returns:
App 实例或 None
"""
@@ -325,11 +436,20 @@ class AppManager:
return None
tenant_bytes = _ToBytes(TenantUUID)
user_bytes = _ToBytes(UserUUID)
company_bytes = _ToBytes(CompanyUUID)
if app.AppType == TYPE_ENTERPRISE:
ta = TenantApp.objects.filter(
TenantUUID=tenant_bytes, AppUUID=app.AppUUID, InstallStatus=1,
).first()
return app if ta else None
if ta:
return app
# 公司维度安装:仅当传了当前公司且该公司已安装
if company_bytes:
ca = CompanyApp.objects.filter(
CompanyUUID=company_bytes, AppUUID=app.AppUUID, InstallStatus=1,
).first()
return app if ca else None
return None
if app.AppType == TYPE_PERSONAL:
ua = UserApp.objects.filter(
UserUUID=user_bytes, AppUUID=app.AppUUID, UseStatus=1,
@@ -339,5 +459,50 @@ class AppManager:
ta = TenantApp.objects.filter(TenantUUID=tenant_bytes, AppUUID=app.AppUUID, InstallStatus=1).first()
if ta:
return app
if company_bytes:
ca = CompanyApp.objects.filter(
CompanyUUID=company_bytes, AppUUID=app.AppUUID, InstallStatus=1,
).first()
if ca:
return app
ua = UserApp.objects.filter(UserUUID=user_bytes, AppUUID=app.AppUUID, UseStatus=1).first()
return app if ua else None
@staticmethod
def GetManageableCompanies(request):
"""返回当前管理员可安装应用的公司 UUID 列表(bytes)
权限范围:
- admin:super —— 全部租户全部公司(跨租户管理员)
- admin:tenant —— 当前租户下全部公司
- admin:company—— 仅被授权(admin:company 绑定 CompanyUUID 维度)的公司
Returns:
(companies: list[bytes], scope: str)
scope: 'all'(跨租户全量)/ 'tenant'(租户内全量)/ 'company'(指定公司)/ ''(无权限)
"""
from gvsdsdk.auth.admin_service import IsSuperAdmin, IsTenantAdmin, IsCompanyAdmin
if IsSuperAdmin(request=request):
from gvsdsdk.models import Company as SdkCompany
return [c.CompanyUUID for c in SdkCompany.objects.filter(CompanyStatus=1)], 'all'
tenant_uuid = getattr(request, 'current_tenant_uuid', None)
tenant_bytes = _ToBytes(tenant_uuid)
if IsTenantAdmin(request=request):
from gvsdsdk.models import Company as SdkCompany
qs = SdkCompany.objects.filter(TenantUUID=tenant_bytes, CompanyStatus=1)
return [c.CompanyUUID for c in qs], 'tenant'
if IsCompanyAdmin(request=request):
# admin:company:取当前角色被授权公司(RolePermission.CompanyUUID 维度)
role = getattr(request, 'current_role', None)
companies = []
if role is not None:
from gvsdsdk.models import RolePermission, Permission
perm_uuids = list(Permission.objects.filter(
PermCode='admin:company', PermStatus=1,
).values_list('PermUUID', flat=True))
for rp in RolePermission.objects.filter(
RoleUUID=role.RoleUUID, PermUUID__in=perm_uuids):
if rp.CompanyUUID and rp.CompanyUUID != b'\x00' * 16:
companies.append(rp.CompanyUUID)
return companies, 'company'
return [], ''
@@ -0,0 +1,34 @@
"""Add CompanyApp (company-dimension app install)
公司维度应用安装——租户可选定一个或多个公司安装(与 TenantApp 全租户安装互补)。
"""
import datetime
from django.db import migrations, models
import gvsds.apps.core.models
class Migration(migrations.Migration):
dependencies = [
('gerp', '0023_app_tenantapp_userapp'),
]
operations = [
migrations.CreateModel(
name='CompanyApp',
fields=[
('company_app_uuid', gvsds.apps.core.models.UUIDField(db_column='CompanyAppUUID', primary_key=True, serialize=False, verbose_name='公司安装记录UUID')),
('company_uuid', gvsds.apps.core.models.UUIDField(db_column='CompanyUUID', verbose_name='公司UUID')),
('app_uuid', gvsds.apps.core.models.UUIDField(db_column='AppUUID', verbose_name='应用UUID')),
('install_status', models.SmallIntegerField(db_column='InstallStatus', default=1, verbose_name='安装状态(1启用/0停用)')),
('install_time', models.DateTimeField(db_column='InstallTime', default=datetime.datetime.utcnow, verbose_name='安装时间')),
('install_by', gvsds.apps.core.models.UUIDField(blank=True, db_column='InstallBy', null=True, verbose_name='安装人UUID')),
],
options={
'db_table': 'CompanyApp',
'verbose_name': '企业应用安装(公司维度)',
'verbose_name_plural': '企业应用安装(公司维度)',
'unique_together': {('company_uuid', 'app_uuid')},
},
),
]
+26
View File
@@ -831,6 +831,32 @@ class TenantApp(QModel):
verbose_name_plural = '企业应用安装'
class CompanyApp(QModel):
"""企业应用安装(公司维度)——租户可选定一个或多个公司安装
与 TenantApp(租户全量安装)互补:
- TenantApp :全租户安装(租户下所有公司生效)
- CompanyApp :指定公司安装(仅该公司生效)
企业功能可用判定:租户已装 或 当前公司已装。
InstallStatus: 1=已安装(启用) / 0=已停用
"""
CompanyAppUUID = UUIDField(primary_key=True, verbose_name='公司安装记录UUID')
CompanyUUID = UUIDField(db_column='CompanyUUID', verbose_name='公司UUID')
AppUUID = UUIDField(db_column='AppUUID', verbose_name='应用UUID')
InstallStatus = models.SmallIntegerField(default=1, verbose_name='安装状态(1启用/0停用)')
InstallTime = models.DateTimeField(default=datetime.datetime.utcnow, verbose_name='安装时间')
InstallBy = UUIDField(null=True, blank=True, verbose_name='安装人UUID')
class Meta:
db_table = 'CompanyApp'
app_label = 'gerp'
unique_together = (('company_uuid', 'app_uuid'),)
verbose_name = '企业应用安装(公司维度)'
verbose_name_plural = '企业应用安装(公司维度)'
class UserApp(QModel):
"""个人应用启用/申请——用户与应用的关系
+1
View File
@@ -147,6 +147,7 @@ from .gitea import (
from .app import (
AppMarketView,
AppMyAppsView,
AppManageCompaniesView,
AppInstallView,
AppUninstallView,
AppUserEnableView,
+121 -13
View File
@@ -58,8 +58,37 @@ def _GetTenantBytes(request):
return None
def _GetCompanyBytes(request):
"""当前角色公司 UUID bytes(CurrentRoleMiddleware 注入)"""
cu = getattr(request, 'current_company_uuid', None)
if cu is None:
return None
if hasattr(cu, 'bytes'):
return cu.bytes
if hasattr(cu, 'pk'):
return cu.pk
try:
return UUIDObj(str(cu)).bytes
except Exception:
return None
def _ParseUUIDList(values):
"""解析公司 UUID 列表(str/bytes 混合 → bytes 列表),过滤无效"""
result = []
for v in (values or []):
if not v:
continue
try:
b = UUIDObj(str(v)).bytes
result.append(b)
except Exception:
continue
return result
class AppMarketView(ELTAuthenticatedAPIView):
"""应用市场——全部启用应用,附租户安装/个人启用/申请状态。"""
"""应用市场——全部启用应用,附租户/公司安装/个人启用/申请状态。"""
permission_classes = [IsAuthenticated]
@@ -67,6 +96,7 @@ class AppMarketView(ELTAuthenticatedAPIView):
data = AppManager.ListMarket(
TenantUUID=_GetTenantBytes(request),
UserUUID=_GetUserBytes(request),
CompanyUUID=_GetCompanyBytes(request),
)
return Response({'code': 0, 'data': {'list': data, 'total': len(data)}})
@@ -80,12 +110,40 @@ class AppMyAppsView(ELTAuthenticatedAPIView):
data = AppManager.ListMyApps(
TenantUUID=_GetTenantBytes(request),
UserUUID=_GetUserBytes(request),
CompanyUUID=_GetCompanyBytes(request),
)
return Response({'code': 0, 'data': {'list': data, 'total': len(data)}})
class AppManageCompaniesView(ELTAuthenticatedAPIView):
"""管理员可安装应用的公司列表(按权限范围:super=全部 / tenant=租户内 / company=被授权公司)"""
permission_classes = [IsAuthenticated, IsAdminUser]
def get(self, request):
companies, scope = AppManager.GetManageableCompanies(request)
rows = []
for c in companies:
from gvsdsdk.models import Company as SdkCompany
company = SdkCompany.objects.filter(CompanyUUID=c).first()
if company is None:
continue
rows.append({
'CompanyUUID': UUIDObj(company.CompanyUUID).str,
'CompanyName': company.CompanyName,
'CompanyShort': company.CompanyShort,
})
return Response({'code': 0, 'data': {'Scope': scope, 'Companies': rows}})
class AppInstallView(ELTAuthenticatedAPIView):
"""租户管理员:安装企业功能。"""
"""管理员:安装企业功能。
支持两种粒度:
- 全租户安装:不传 CompanyUUIDs(或传空)→ 写入 TenantApp,租户全员可用
- 指定公司安装:传 CompanyUUIDs(一个或多个)→ 写入 CompanyApp,仅这些公司可用
权限范围由 AppManager.GetManageableCompanies 校验(super/tenant/company)。
"""
permission_classes = [IsAuthenticated, IsAdminUser]
@@ -93,17 +151,45 @@ class AppInstallView(ELTAuthenticatedAPIView):
app_uuid = (request.data.get('AppUUID') or '').strip()
if not app_uuid:
return Response({'code': 1, 'msg': '缺少应用 UUID'}, status=400)
tenant_bytes = _GetTenantBytes(request)
if not tenant_bytes:
return Response({'code': 1, 'msg': '无法确定当前租户'}, status=400)
ok, err, flat = AppManager.InstallTenantApp(tenant_bytes, app_uuid)
# 公司维度目标(可选):不传则全租户安装
company_uuids = _ParseUUIDList(request.data.get('CompanyUUIDs'))
# 全租户安装:校验租户上下文
if not company_uuids:
tenant_bytes = _GetTenantBytes(request)
if not tenant_bytes:
return Response({'code': 1, 'msg': '无法确定当前租户'}, status=400)
ok, err, flat = AppManager.InstallTenantApp(tenant_bytes, app_uuid)
if not ok:
return Response({'code': 1, 'msg': err}, status=400)
return Response({'code': 0, 'msg': '安装成功(全租户)', 'data': flat})
# 指定公司安装:校验目标公司都在管理员可管理范围内
manageable, scope = AppManager.GetManageableCompanies(request)
if not manageable:
return Response({'code': 403, 'msg': '无权安装应用:您没有可管理的公司'}, status=403)
manageable_set = {c.hex for c in manageable}
company_bytes = _ParseUUIDList(request.data.get('CompanyUUIDs'))
invalid = [c.hex for c in company_bytes if c.hex not in manageable_set]
if invalid:
return Response({'code': 403, 'msg': '无权为以下公司安装(不在您的管理范围)'}, status=403)
ok, err, count = AppManager.InstallCompanyApps(
company_uuids, app_uuid, InstallBy=_GetUserBytes(request),
)
if not ok:
return Response({'code': 1, 'msg': err}, status=400)
return Response({'code': 0, 'msg': '安装成功', 'data': flat})
return Response({'code': 0, 'msg': f'安装成功({count} 家公司)', 'data': {'InstalledCount': count}})
class AppUninstallView(ELTAuthenticatedAPIView):
"""租户管理员:停用企业功能。"""
"""管理员:停用企业功能。
支持两种粒度:
- 传 CompanyUUIDs → 停用指定公司安装(CompanyApp)
- 不传 CompanyUUIDs → 停用全租户安装(TenantApp)+ 停用当前公司安装(CompanyApp)
(前端停用当前公司功能时无需知道当前公司 UUID,后端按当前上下文双停)
"""
permission_classes = [IsAuthenticated, IsAdminUser]
@@ -111,12 +197,34 @@ class AppUninstallView(ELTAuthenticatedAPIView):
app_uuid = (request.data.get('AppUUID') or '').strip()
if not app_uuid:
return Response({'code': 1, 'msg': '缺少应用 UUID'}, status=400)
company_uuids = _ParseUUIDList(request.data.get('CompanyUUIDs'))
# 停用指定公司安装
if company_uuids:
manageable, scope = AppManager.GetManageableCompanies(request)
if not manageable:
return Response({'code': 403, 'msg': '无权停用应用:您没有可管理的公司'}, status=403)
manageable_set = {c.hex for c in manageable}
company_bytes = _ParseUUIDList(request.data.get('CompanyUUIDs'))
invalid = [c.hex for c in company_bytes if c.hex not in manageable_set]
if invalid:
return Response({'code': 403, 'msg': '无权为以下公司停用(不在您的管理范围)'}, status=403)
ok, err = AppManager.UninstallCompanyApps(company_uuids, app_uuid)
if not ok:
return Response({'code': 1, 'msg': err}, status=400)
return Response({'code': 0, 'msg': '已停用(指定公司)'})
# 停用全租户安装 + 当前公司安装(前端停用当前公司功能时双停)
tenant_bytes = _GetTenantBytes(request)
if not tenant_bytes:
return Response({'code': 1, 'msg': '无法确定当前租户'}, status=400)
ok, err = AppManager.UninstallTenantApp(tenant_bytes, app_uuid)
if not ok:
return Response({'code': 1, 'msg': err}, status=400)
if tenant_bytes:
AppManager.UninstallTenantApp(tenant_bytes, app_uuid)
company_bytes = _GetCompanyBytes(request)
if company_bytes:
try:
AppManager.UninstallCompanyApps([company_bytes], app_uuid)
except Exception:
pass
return Response({'code': 0, 'msg': '已停用'})
+4 -1
View File
@@ -176,7 +176,10 @@ class MenuListCompatView(ELTAuthenticatedAPIView):
a for a in App.objects.filter(AppStatus=1, MenuRootUUID__isnull=False)
]
for _app in app_roots:
if AppManager.CheckAccess(current_tenant_uuid, user.UserUUID, _app.AppCode) is None:
if AppManager.CheckAccess(
current_tenant_uuid, user.UserUUID, _app.AppCode,
CompanyUUID=current_company,
) is None:
root_str = UUIDObj(_app.MenuRootUUID).str
if root_str not in menu_map:
continue
+1
View File
@@ -149,6 +149,7 @@ urlpatterns = [
# 应用/功能市场与安装授权
path('API/App/Market', gerp_views.AppMarketView.as_view(), name='app_market'),
path('API/App/MyApps', gerp_views.AppMyAppsView.as_view(), name='app_my_apps'),
path('API/App/ManageCompanies', gerp_views.AppManageCompaniesView.as_view(), name='app_manage_companies'),
path('API/App/Install', gerp_views.AppInstallView.as_view(), name='app_install'),
path('API/App/Uninstall', gerp_views.AppUninstallView.as_view(), name='app_uninstall'),
path('API/App/Enable', gerp_views.AppUserEnableView.as_view(), name='app_enable'),