Skip to content

Commit b1db2e0

Browse files
committed
feat: support system variable
1 parent fb6dfed commit b1db2e0

File tree

16 files changed

+447
-50
lines changed

16 files changed

+447
-50
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""062_system_variable
2+
3+
Revision ID: ed947895d470
4+
Revises: 547df942eb90
5+
Create Date: 2026-01-26 10:16:59.877303
6+
7+
"""
8+
import sqlalchemy as sa
9+
import sqlmodel.sql.sqltypes
10+
from alembic import op
11+
from sqlalchemy import String, BigInteger, DateTime
12+
from sqlalchemy.dialects import postgresql
13+
from sqlalchemy.dialects.postgresql import JSONB
14+
from sqlalchemy.sql import table, column
15+
16+
# revision identifiers, used by Alembic.
17+
revision = 'ed947895d470'
18+
down_revision = '547df942eb90'
19+
branch_labels = None
20+
depends_on = None
21+
22+
23+
def upgrade():
24+
# ### commands auto generated by Alembic - please adjust! ###
25+
op.create_table('system_variable',
26+
sa.Column('id', sa.BigInteger(), sa.Identity(always=True), nullable=False),
27+
sa.Column('name', sqlmodel.sql.sqltypes.AutoString(length=128), nullable=False),
28+
sa.Column('var_type', sqlmodel.sql.sqltypes.AutoString(length=128), nullable=False),
29+
sa.Column('type', sqlmodel.sql.sqltypes.AutoString(length=128), nullable=False),
30+
sa.Column('value', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
31+
sa.Column('create_time', sa.DateTime(), nullable=True),
32+
sa.Column('create_by', sa.BigInteger(), nullable=True),
33+
sa.PrimaryKeyConstraint('id')
34+
)
35+
36+
variable_table = table(
37+
"system_variable",
38+
column("id", BigInteger),
39+
column("name", String),
40+
column("var_type", String),
41+
column("type", String),
42+
column("value", JSONB),
43+
column("create_time", DateTime),
44+
column("create_by", BigInteger),
45+
)
46+
47+
op.bulk_insert(
48+
variable_table,
49+
[
50+
{
51+
"name": "i18n_variable.name",
52+
"var_type": "text",
53+
"type": "system",
54+
"value": ["name"],
55+
"create_time": None,
56+
"create_by": None
57+
},
58+
{
59+
"name": "i18n_variable.account",
60+
"var_type": "text",
61+
"type": "system",
62+
"value": ["account"],
63+
"create_time": None,
64+
"create_by": None
65+
},
66+
{
67+
"name": "i18n_variable.email",
68+
"var_type": "text",
69+
"type": "system",
70+
"value": ["email"],
71+
"create_time": None,
72+
"create_by": None
73+
}
74+
]
75+
)
76+
# ### end Alembic commands ###
77+
78+
79+
def downgrade():
80+
# ### commands auto generated by Alembic - please adjust! ###
81+
op.drop_table('system_variable')
82+
# ### end Alembic commands ###
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""063_user
2+
3+
Revision ID: 8ff90df7871d
4+
Revises: ed947895d470
5+
Create Date: 2026-01-26 15:30:11.348083
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
from sqlalchemy.dialects import postgresql
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '8ff90df7871d'
15+
down_revision = 'ed947895d470'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.add_column('sys_user', sa.Column('system_variables', postgresql.JSONB(astext_type=sa.Text()), nullable=True))
23+
# ### end Alembic commands ###
24+
25+
26+
def downgrade():
27+
# ### commands auto generated by Alembic - please adjust! ###
28+
op.drop_column('sys_user', 'system_variables')
29+
# ### end Alembic commands ###

backend/apps/api.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from apps.data_training.api import data_training
66
from apps.datasource.api import datasource, table_relation, recommended_problem
77
from apps.mcp import mcp
8-
from apps.system.api import login, user, aimodel, workspace, assistant, parameter, apikey
8+
from apps.system.api import login, user, aimodel, workspace, assistant, parameter, apikey, variable_api
99
from apps.terminology.api import terminology
1010
from apps.settings.api import base
1111
#from audit.api import audit_api
@@ -30,4 +30,6 @@
3030

3131
api_router.include_router(recommended_problem.router)
3232

33+
api_router.include_router(variable_api.router)
34+
3335
#api_router.include_router(audit_api.router)

backend/apps/datasource/crud/permission.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def get_row_permission_filters(session: SessionDep, current_user: CurrentUser, d
4040
break
4141
if flag:
4242
res.append(transRecord2DTO(session, permission))
43-
where_str = transFilterTree(session, res, ds)
43+
where_str = transFilterTree(session, current_user, res, ds)
4444
filters.append({"table": table.table_name, "filter": where_str})
4545
return filters
4646

backend/apps/datasource/crud/row_permission.py

Lines changed: 129 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,26 @@
44
from typing import List, Dict
55
from apps.datasource.models.datasource import CoreField, CoreDatasource
66
from apps.db.constant import DB
7-
from common.core.deps import SessionDep
7+
from apps.system.models.system_variable_model import SystemVariable
8+
from common.core.deps import SessionDep, CurrentUser
89

910

10-
def transFilterTree(session: SessionDep, tree_list: List[any], ds: CoreDatasource) -> str | None:
11+
def transFilterTree(session: SessionDep, current_user: CurrentUser, tree_list: List[any],
12+
ds: CoreDatasource) -> str | None:
1113
if tree_list is None:
1214
return None
1315
res: List[str] = []
1416
for dto in tree_list:
1517
tree = dto.tree
1618
if tree is None:
1719
continue
18-
tree_exp = transTreeToWhere(session, tree, ds)
20+
tree_exp = transTreeToWhere(session, current_user, tree, ds)
1921
if tree_exp is not None:
2022
res.append(tree_exp)
2123
return " AND ".join(res)
2224

2325

24-
def transTreeToWhere(session: SessionDep, tree: any, ds: CoreDatasource) -> str | None:
26+
def transTreeToWhere(session: SessionDep, current_user: CurrentUser, tree: any, ds: CoreDatasource) -> str | None:
2527
if tree is None:
2628
return None
2729
logic = tree['logic']
@@ -32,23 +34,25 @@ def transTreeToWhere(session: SessionDep, tree: any, ds: CoreDatasource) -> str
3234
for item in items:
3335
exp: str = None
3436
if item['type'] == 'item':
35-
exp = transTreeItem(session, item, ds)
37+
exp = transTreeItem(session, current_user, item, ds)
3638
elif item['type'] == 'tree':
37-
exp = transTreeToWhere(session, item['sub_tree'], ds)
39+
exp = transTreeToWhere(session, current_user, item['sub_tree'], ds)
3840

3941
if exp is not None:
4042
list.append(exp)
4143
return '(' + f' {logic} '.join(list) + ')' if len(list) > 0 else None
4244

4345

44-
def transTreeItem(session: SessionDep, item: Dict, ds: CoreDatasource) -> str | None:
46+
def transTreeItem(session: SessionDep, current_user: CurrentUser, item: Dict, ds: CoreDatasource) -> str | None:
4547
res: str = None
4648
field = session.query(CoreField).filter(CoreField.id == int(item['field_id'])).first()
4749
if field is None:
4850
return None
4951

5052
db = DB.get_db(ds.type)
5153
whereName = db.prefix + field.field_name + db.suffix
54+
whereTerm = transFilterTerm(item['term'])
55+
5256
if item['filter_type'] == 'enum':
5357
if len(item['enum_value']) > 0:
5458
if ds['type'] == 'sqlServer' and (
@@ -57,38 +61,113 @@ def transTreeItem(session: SessionDep, item: Dict, ds: CoreDatasource) -> str |
5761
else:
5862
res = "(" + whereName + " IN ('" + "','".join(item['enum_value']) + "'))"
5963
else:
60-
value = item['value']
61-
whereTerm = transFilterTerm(item['term'])
62-
whereValue = ''
64+
# if system variable, do check and get value
65+
# new field: value_type(variable or normal), variable_id
66+
value_type = item.get('value_type')
67+
if value_type and value_type == 'variable':
68+
# get system variable
69+
variable_id = item.get('variable_id')
70+
if variable_id is not None:
71+
sys_variable = session.query(SystemVariable).filter(SystemVariable.id == variable_id).first()
72+
# do inner system variable
73+
if sys_variable.type == 'system':
74+
res = whereName + whereTerm + getSysVariableValue(sys_variable, current_user)
75+
else:
76+
# check user variable
77+
user_variables = current_user.system_variables
78+
if user_variables is None or len(user_variables) == 0 or not userHaveVariable(user_variables,
79+
sys_variable):
80+
return None
81+
else:
82+
# get user variable
83+
u_variable = None
84+
for u in user_variables:
85+
if u.get('variableId') == sys_variable.id:
86+
u_variable = u
87+
break
88+
if u_variable is None:
89+
return None
6390

64-
if item['term'] == 'null':
65-
whereValue = ''
66-
elif item['term'] == 'not_null':
67-
whereValue = ''
68-
elif item['term'] == 'empty':
69-
whereValue = "''"
70-
elif item['term'] == 'not_empty':
71-
whereValue = "''"
72-
elif item['term'] == 'in' or item['term'] == 'not in':
73-
if ds.type == 'sqlServer' and (
74-
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
75-
whereValue = "(N'" + "', N'".join(value.split(",")) + "')"
76-
else:
77-
whereValue = "('" + "', '".join(value.split(",")) + "')"
78-
elif item['term'] == 'like' or item['term'] == 'not like':
79-
if ds.type == 'sqlServer' and (
80-
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
81-
whereValue = f"N'%{value}%'"
91+
# check value
92+
values = u_variable.get('variableValues')
93+
if sys_variable.var_type == 'text':
94+
set_sys = set(sys_variable.value)
95+
values = [x for x in values if x in set_sys]
96+
if values is None or len(values) == 0:
97+
return None
98+
elif sys_variable.var_type == 'number':
99+
if (sys_variable.value[0] is not None and values[0] < sys_variable.value[0]) or (
100+
sys_variable.value[1] is not None and values[0] > sys_variable.value[1]):
101+
return None
102+
elif sys_variable.var_type == 'datetime':
103+
if (sys_variable.value[0] is not None and values[0] < sys_variable.value[0]) or (
104+
sys_variable.value[1] is not None and values[0] > sys_variable.value[1]):
105+
return None
106+
107+
# build exp
108+
whereValue = ''
109+
if item['term'] == 'null':
110+
whereValue = ''
111+
elif item['term'] == 'not_null':
112+
whereValue = ''
113+
elif item['term'] == 'empty':
114+
whereValue = "''"
115+
elif item['term'] == 'not_empty':
116+
whereValue = "''"
117+
elif item['term'] == 'in' or item['term'] == 'not in':
118+
if ds.type == 'sqlServer' and (
119+
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
120+
whereValue = "(N'" + "', N'".join(values) + "')"
121+
else:
122+
whereValue = "('" + "', '".join(values) + "')"
123+
elif item['term'] == 'like' or item['term'] == 'not like':
124+
if ds.type == 'sqlServer' and (
125+
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
126+
whereValue = f"N'%{values[0]}%'"
127+
else:
128+
whereValue = f"'%{values[0]}%'"
129+
else:
130+
if ds.type == 'sqlServer' and (
131+
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
132+
whereValue = f"N'{values[0]}'"
133+
else:
134+
whereValue = f"'{values[0]}'"
135+
136+
res = whereName + whereTerm + whereValue
82137
else:
83-
whereValue = f"'%{value}%'"
138+
return None
84139
else:
85-
if ds.type == 'sqlServer' and (
86-
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
87-
whereValue = f"N'{value}'"
140+
value = item['value']
141+
whereValue = ''
142+
143+
if item['term'] == 'null':
144+
whereValue = ''
145+
elif item['term'] == 'not_null':
146+
whereValue = ''
147+
elif item['term'] == 'empty':
148+
whereValue = "''"
149+
elif item['term'] == 'not_empty':
150+
whereValue = "''"
151+
elif item['term'] == 'in' or item['term'] == 'not in':
152+
if ds.type == 'sqlServer' and (
153+
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
154+
whereValue = "(N'" + "', N'".join(value.split(",")) + "')"
155+
else:
156+
whereValue = "('" + "', '".join(value.split(",")) + "')"
157+
elif item['term'] == 'like' or item['term'] == 'not like':
158+
if ds.type == 'sqlServer' and (
159+
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
160+
whereValue = f"N'%{value}%'"
161+
else:
162+
whereValue = f"'%{value}%'"
88163
else:
89-
whereValue = f"'{value}'"
164+
if ds.type == 'sqlServer' and (
165+
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
166+
whereValue = f"N'{value}'"
167+
else:
168+
whereValue = f"'{value}'"
90169

91-
res = whereName + whereTerm + whereValue
170+
res = whereName + whereTerm + whereValue
92171
return res
93172

94173

@@ -124,3 +203,19 @@ def transFilterTerm(term: str) -> str:
124203
if term == "between":
125204
return " BETWEEN "
126205
return ""
206+
207+
208+
def userHaveVariable(user_variables: List, sys_variable: SystemVariable):
209+
for u in user_variables:
210+
if sys_variable.id == u.get('variableId'):
211+
return True
212+
return False
213+
214+
215+
def getSysVariableValue(sys_variable: SystemVariable, current_user: CurrentUser):
216+
if sys_variable.value[0] == 'name':
217+
return current_user.name
218+
if sys_variable.value[0] == 'account':
219+
return current_user.account
220+
if sys_variable.value[0] == 'email':
221+
return current_user.email

backend/apps/swagger/i18n.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ def load_translation(lang: str) -> Dict[str, str]:
107107
{
108108
"name": "Audit",
109109
"description": f"{PLACEHOLDER_PREFIX}audit_api"
110+
},
111+
{
112+
"name": "System_variable",
113+
"description": f"{PLACEHOLDER_PREFIX}variable_api"
110114
}
111115
]
112116

backend/apps/swagger/locales/en.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,5 +188,11 @@
188188
"delete_resource_api": "Delete Resource",
189189
"create_canvas_api": "Create Dashboard",
190190
"update_canvas_api": "Update Dashboard",
191-
"check_name_api": "Name Validation"
191+
"check_name_api": "Name Validation",
192+
193+
"variable_api": "System Variable",
194+
"variable_save": "Save",
195+
"variable_delete": "Delete",
196+
"variable_list": "List",
197+
"variable_page": "Pager"
192198
}

backend/apps/swagger/locales/zh.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,5 +188,11 @@
188188
"delete_resource_api": "删除资源",
189189
"create_canvas_api": "新建仪表板",
190190
"update_canvas_api": "更新仪表板",
191-
"check_name_api": "名称校验"
191+
"check_name_api": "名称校验",
192+
193+
"variable_api": "系统变量",
194+
"variable_save": "保存变量",
195+
"variable_delete": "删除变量",
196+
"variable_list": "获取变量",
197+
"variable_page": "获取变量分页"
192198
}

0 commit comments

Comments
 (0)