feat: add scoped device catalog and unlimited custom area hierarchy

This commit is contained in:
Codex
2026-10-04 11:19:14 +08:00
parent 72a3252e25
commit 5544780bf0
16 changed files with 349 additions and 66 deletions
+36 -1
View File
@@ -6,7 +6,7 @@ import secrets
class Households:
TABLES = ('sites', 'assets', 'recorders', 'cameras', 'people', 'family_links', 'family_events', 'users')
TABLES = ('sites', 'assets', 'recorders', 'cameras', 'devices', 'people', 'family_links', 'family_events', 'users')
def __init__(self, app):
self.a = app
@@ -19,6 +19,7 @@ class Households:
a = self.a
with a.LOCK, a.DB:
a.DB.execute('CREATE TABLE IF NOT EXISTS households (id TEXT PRIMARY KEY, body TEXT NOT NULL)')
a.DB.execute('CREATE TABLE IF NOT EXISTS devices (id TEXT PRIMARY KEY, body TEXT NOT NULL)')
default = a.setting('defaultHousehold')
if not default:
default = secrets.token_hex(8)
@@ -69,6 +70,22 @@ class Households:
family = self.scope(user)
return [x for x in self.a.objects(table) if self.family_of(x) == family]
def site_ids(self, user):
"""Optional branch grants, always restricted to the account's own organization."""
sites = self.objects(user, 'sites')
ids = {s['id'] for s in sites if s['id'] in user.get('siteIds', [])}
if user.get('includeSubspaces', False):
children = {}
for site in sites:
children.setdefault(site.get('parentId'), []).append(site['id'])
queue = list(ids)
for parent in queue:
for child in children.get(parent, []):
if child not in ids:
ids.add(child)
queue.append(child)
return ids
def prepare(self, user, table, body, references=()):
family = self.scope(user)
if body.get('familyId', family) != family:
@@ -137,8 +154,12 @@ class Households:
raise a.Problem('家庭不存在', 404)
if user['role'] != 'admin' and (not old or old['id'] != self.scope(user)):
raise a.Problem('只能管理自己所属的家庭', 403)
kind = data.get('kind', old.get('kind', 'home') if old else 'home')
if kind not in ('home', 'office', 'factory'):
raise a.Problem('请选择家庭、办公场所或工厂')
item = dict(old or {}, id=old['id'] if old else secrets.token_hex(8),
name=a.clean_text(data.get('name', ''), 80, True),
kind=kind,
note=a.clean_text(data.get('note', ''), 300),
createdAt=old['createdAt'] if old else dt.datetime.now(dt.timezone.utc).isoformat())
a.save_object('households', item)
@@ -148,3 +169,17 @@ class Households:
def revision_key(self, user):
family = self.scope(user)
return 'familyRevision' if family == self.a.setting('defaultHousehold') else 'familyRevision:' + family
def save_view(self, data, user):
if not self.manager(user):
raise self.a.Problem('需要场所管理员权限', 403)
groups = data.get('groups')
if (not isinstance(groups, list) or len(groups) > 5 or
any(g not in ('type', 'space', 'manager', 'owner', 'tag') for g in groups) or len(set(groups)) != len(groups)):
raise self.a.Problem('分组维度不可重复,请选择类型、区域、负责人、所有人或标签')
with self.a.LOCK:
home = self.a.get_object('households', self.scope(user))
home['deviceGrouping'] = groups
self.a.save_object('households', home)
self.a.audit('更新设备分组方式', home['name'])
return {'ok': True}