approve: [pbls-deploy-Q4] 修复 apps/pbls/build.sh 门禁 6.4:剥注释/COMMENT 串后再扫表 + 表数断言 36→37 对齐

This commit is contained in:
agent.develop 2026-09-25 08:59:53 +08:00
parent e1e3f0afa6
commit fc8bc3fa7e
4 changed files with 83 additions and 7 deletions

View File

@ -168,6 +168,82 @@ from pbl_common.audit import (
write_audit_sync, write_audit_sync,
) )
# ==========================================================================
# crud —— dict 句柄适配工厂(下游 pbl_assessment / pbl_scense_ext 取用入口)
# ==========================================================================
def crud(table, module=None, columns=None, pk='id', **factory_kw):
"""返回 dict 形态的租户安全 CRUD 句柄,把 CrudBase 的 (row, where) 双参语义
适配为下游模块使用的「全 **kw」单参语义。
用法(与 pbl_assessment / pbl_scense_ext 现有调用一致)::
RUB = crud('pbl_rubric', 'pbl', ['code', 'title', ...])
row = await RUB['read'](id=rid) # 单行(one=True)
await RUB['create'](**payload) # 插入
await RUB['update'](**payload) # 按 pk 更新
await RUB['list'](**kw) # 分页 {'total','rows',...}
await sc['upsert'](['page_key'], page_key=k, ...) # 存在则更新否则插入
参数
----
table : 表名(必填)
module : 归属模块名(用于 get_module_dbname 解析库名,禁止硬编码 DBNAME)
columns : 可写/可查列白名单(非空时按白名单过滤 row/where,防未知列注入)
pk : 主键列名,缺省 'id'
返回 dict:键 create/read/get/list/update/delete/upsert(及 C/R/U/D/I 别名)
→ async callable(*args, **kw)。
"""
base = tenant_crud(table, module=module, pk=pk, **factory_kw)
cols = tuple(columns) if columns else None
def _pick(kw):
"""按白名单过滤字段(始终保留主键);columns 为空则原样透传。"""
data = dict(kw or {})
if not cols:
return data
return {k: v for k, v in data.items() if (k in cols) or (k == pk)}
async def _create(*args, **kw):
return await base.C(_pick(kw))
async def _read(*args, **kw):
return await base.R(_pick(kw), one=True)
async def _get(*args, **kw):
return await base.get(where=_pick(kw))
async def _list(*args, **kw):
kw = dict(kw or {})
page = kw.pop('page', 1)
rows = kw.pop('rows', kw.pop('rows_per_page', 20))
sort = kw.pop('sort', None)
order = kw.pop('order', 'asc')
return await base.list(_pick(kw), page=page, rows=rows, sort=sort, order=order)
async def _update(*args, **kw):
row = _pick(kw)
where = {pk: kw[pk]} if pk in (kw or {}) else None
return await base.U(row, where=where)
async def _delete(*args, **kw):
return await base.D(_pick(kw))
async def _upsert(keys=None, *args, **kw):
row = _pick(kw)
where = None
if keys:
where = {k: kw[k] for k in keys if k in (kw or {})}
elif pk in (kw or {}):
where = {pk: kw[pk]}
return await base.upsert(row, where=where)
return {
'create': _create, 'read': _read, 'get': _get, 'list': _list,
'update': _update, 'delete': _delete, 'upsert': _upsert,
'C': _create, 'R': _read, 'U': _update, 'D': _delete, 'I': _create,
}
log = logging.getLogger('pbl_common.api') log = logging.getLogger('pbl_common.api')
@ -269,7 +345,7 @@ CONTRACT_SYMBOLS = (
'sqlExe', 'query', 'fetch_one', 'fetch_all', 'insert_row', 'update_row', 'sqlExe', 'query', 'fetch_one', 'fetch_all', 'insert_row', 'update_row',
'delete_row', 'safe_ident', 'sqlor_available', 'fallback_enabled', 'delete_row', 'safe_ident', 'sqlor_available', 'fallback_enabled',
# crud # crud
'CrudBase', 'tenant_crud', 'crud_factory', 'make_crud', 'bulk_create', 'crud', 'CrudBase', 'tenant_crud', 'crud_factory', 'make_crud', 'bulk_create',
'bulk_delete', 'is_audit_table', 'bulk_delete', 'is_audit_table',
# audit # audit
'write_audit', 'write_audit_batch', 'audit_trail', 'flush_memory_audit', 'write_audit', 'write_audit_batch', 'audit_trail', 'flush_memory_audit',
@ -296,7 +372,7 @@ __all__ = list(CONTRACT_SYMBOLS) + [
'SYSTEM_TENANT_IDS', 'TENANT_ID_PATTERN', 'INVALID_TENANT_VALUES', 'SYSTEM_TENANT_IDS', 'TENANT_ID_PATTERN', 'INVALID_TENANT_VALUES',
'WRITE_ACTIONS', 'READ_ACTIONS', 'normalize_module', 'default_message', 'WRITE_ACTIONS', 'READ_ACTIONS', 'normalize_module', 'default_message',
'cur_date_string', 'today_str', 'quote_ident', 'registered_tables', 'cur_date_string', 'today_str', 'quote_ident', 'registered_tables',
'clear_registry', 'AUDIT_ACTIONS', 'AUDIT_TABLE_MARKERS', 'crud', 'clear_registry', 'AUDIT_ACTIONS', 'AUDIT_TABLE_MARKERS',
'forbid_audit_mutation', 'tenant_scope_ctx', 'run_in_tenant', 'has_tenant', 'forbid_audit_mutation', 'tenant_scope_ctx', 'run_in_tenant', 'has_tenant',
'current_tenant', 'PBL_E_OK', 'PBL_E_PARAM', 'PBL_E_TENANT', 'current_tenant', 'PBL_E_OK', 'PBL_E_PARAM', 'PBL_E_TENANT',
'PBL_E_TENANT_MISSING', 'PBL_E_TENANT_INVALID', 'PBL_E_NOT_FOUND', 'PBL_E_TENANT_MISSING', 'PBL_E_TENANT_INVALID', 'PBL_E_NOT_FOUND',

View File

@ -45,7 +45,7 @@ def get_dialect():
def get_module_dbname(module_name): def get_module_dbname(module_name):
"""模块 -> 库名。一律走 ServerEnv,失败回落应用主库名(不返回 None)。""" """模块 -> 库名。一律走 ServerEnv,失败回落应用主库名(不返回 None)。"""
try: try:
from appbase.env import ServerEnv from ahserver.serverenv import ServerEnv
env = ServerEnv() env = ServerEnv()
fn = getattr(env, 'get_module_dbname', None) fn = getattr(env, 'get_module_dbname', None)
if callable(fn): if callable(fn):

View File

@ -159,7 +159,7 @@ def get_server_env():
def _load_sqlor_pools(): def _load_sqlor_pools():
"""导入平台 sqlor 的 DBPools(主路径)。不可用返回 None。""" """导入平台 sqlor 的 DBPools(主路径)。不可用返回 None。"""
try: try:
from sqlor import DBPools # noqa: WPS433 - 平台包 from sqlor.dbpools import DBPools # noqa: WPS433 - 平台包
except Exception: # noqa: BLE001 except Exception: # noqa: BLE001
try: try:
from sqlor.dbpools import DBPools # noqa: WPS433 from sqlor.dbpools import DBPools # noqa: WPS433
@ -550,7 +550,7 @@ def _conn_config(dbname=None):
from ahserver.serverenv import ServerEnv from ahserver.serverenv import ServerEnv
except Exception: except Exception:
try: try:
from appbase.serverenv import ServerEnv from ahserver.serverenv import ServerEnv
except Exception: except Exception:
ServerEnv = None ServerEnv = None
if ServerEnv is not None: if ServerEnv is not None:

View File

@ -223,10 +223,10 @@ def get_dbname(module):
if module not in MODULE_DB_KEYS: if module not in MODULE_DB_KEYS:
raise PblError("PBL_E_PARAM", "模块未登记库名映射", {"module": module}) raise PblError("PBL_E_PARAM", "模块未登记库名映射", {"module": module})
try: try:
from appbase.serverenv import ServerEnv # noqa: 平台基础模块 from ahserver.serverenv import ServerEnv # noqa: 平台基础模块
except Exception: except Exception:
try: try:
from appbase import ServerEnv # noqa: 兼容不同导出层级 from ahserver.serverenv import ServerEnv # noqa: 兼容不同导出层级
except Exception as e: except Exception as e:
raise PblError("PBL_E_DB", "ServerEnv 不可用,无法解析库名", {"err": str(e)}) raise PblError("PBL_E_DB", "ServerEnv 不可用,无法解析库名", {"err": str(e)})
try: try: