fix(entcms): 重写所有数据库操作使用 async with context manager

_get_db() 返回的是 async context manager,不是 sor 对象。
所有函数都错误地直接调用 sor.R/C/U/D,导致 AttributeError。

修正为正确模式:
  db = DBPools()
  async with db.sqlorContext(DBNAME) as sor:
      await sor.R('table', ns)

影响函数:
- cms_content CRUD (4个)
- cms_categories CRUD (4个) + get_category_options
- cms_leads CRUD (4个) + submit_lead
- cms_sections CRUD (4个) + get_visible_sections
- cms_site_config CRUD (4个) + get_site_config
- get_published_content, get_latest_news, get_content_detail
- submit_content_for_approval
This commit is contained in:
yumoqing 2026-06-03 17:24:55 +08:00
parent 2a49ed80d1
commit 7abdc336b7

View File

@ -5,6 +5,7 @@ entcms - 企业CMS系统模块
import json
from ahserver.serverenv import ServerEnv
from appPublic.uniqueID import getID
from sqlor.dbpools import DBPools
MODULE_NAME = "entcms"
MODULE_VERSION = "1.0.0"
@ -12,152 +13,165 @@ MODULE_VERSION = "1.0.0"
DBNAME = "entcms"
def _get_db():
"""获取数据库上下文"""
from sqlor.dbpools import DBPools
return DBPools().sqlorContext(DBNAME)
# ===== CMS Content CRUD =====
async def cms_content_list(ns=None):
"""查询内容列表"""
sor = _get_db()
ns = ns or {}
ns.setdefault('sort', 'sort_order asc, created_at desc')
rows = await sor.R('cms_content', ns)
total = len(rows)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = ns or {}
ns.setdefault('sort', 'sort_order asc, created_at desc')
rows = await sor.R('cms_content', ns)
total = len(rows)
return {'rows': rows, 'total': total}
async def cms_content_create(data):
"""创建内容"""
sor = _get_db()
data['id'] = getID()
await sor.C('cms_content', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
data['id'] = getID()
await sor.C('cms_content', data)
return data
async def cms_content_update(data):
"""更新内容"""
sor = _get_db()
await sor.U('cms_content', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.U('cms_content', data)
return data
async def cms_content_delete(data):
"""删除内容"""
sor = _get_db()
await sor.D('cms_content', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.D('cms_content', data)
return data
# ===== CMS Categories CRUD =====
async def cms_categories_list(ns=None):
"""查询分类列表"""
sor = _get_db()
ns = ns or {}
ns.setdefault('sort', 'sort_order asc')
rows = await sor.R('cms_categories', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = ns or {}
ns.setdefault('sort', 'sort_order asc')
rows = await sor.R('cms_categories', ns)
return {'rows': rows, 'total': len(rows)}
async def cms_categories_create(data):
sor = _get_db()
data['id'] = getID()
await sor.C('cms_categories', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
data['id'] = getID()
await sor.C('cms_categories', data)
return data
async def cms_categories_update(data):
sor = _get_db()
await sor.U('cms_categories', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.U('cms_categories', data)
return data
async def cms_categories_delete(data):
sor = _get_db()
await sor.D('cms_categories', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.D('cms_categories', data)
return data
async def get_category_options(content_type=None):
"""获取分类下拉选项"""
sor = _get_db()
ns = {'sort': 'sort_order asc'}
if content_type:
ns['content_type'] = content_type
rows = await sor.R('cms_categories', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = {'sort': 'sort_order asc'}
if content_type:
ns['content_type'] = content_type
rows = await sor.R('cms_categories', ns)
options = [{'value': r['id'], 'text': r['name']} for r in rows]
return options
# ===== CMS Leads CRUD =====
async def cms_leads_list(ns=None):
sor = _get_db()
ns = ns or {}
ns.setdefault('sort', 'created_at desc')
rows = await sor.R('cms_leads', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = ns or {}
ns.setdefault('sort', 'created_at desc')
rows = await sor.R('cms_leads', ns)
return {'rows': rows, 'total': len(rows)}
async def cms_leads_create(data):
sor = _get_db()
data['id'] = getID()
await sor.C('cms_leads', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
data['id'] = getID()
await sor.C('cms_leads', data)
return data
async def cms_leads_update(data):
sor = _get_db()
await sor.U('cms_leads', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.U('cms_leads', data)
return data
async def cms_leads_delete(data):
sor = _get_db()
await sor.D('cms_leads', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.D('cms_leads', data)
return data
async def submit_lead(data):
"""公开接口 - 网站访客提交线索"""
sor = _get_db()
data['id'] = getID()
data.setdefault('status', 'new')
data.setdefault('source', 'website')
await sor.C('cms_leads', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
data['id'] = getID()
data.setdefault('status', 'new')
data.setdefault('source', 'website')
await sor.C('cms_leads', data)
return {'status': 'ok', 'id': data['id']}
# ===== CMS Sections CRUD =====
async def cms_sections_list(ns=None):
sor = _get_db()
ns = ns or {}
ns.setdefault('sort', 'sort_order asc')
rows = await sor.R('cms_sections', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = ns or {}
ns.setdefault('sort', 'sort_order asc')
rows = await sor.R('cms_sections', ns)
return {'rows': rows, 'total': len(rows)}
async def cms_sections_create(data):
sor = _get_db()
data['id'] = getID()
await sor.C('cms_sections', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
data['id'] = getID()
await sor.C('cms_sections', data)
return data
async def cms_sections_update(data):
sor = _get_db()
await sor.U('cms_sections', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.U('cms_sections', data)
return data
async def cms_sections_delete(data):
sor = _get_db()
await sor.D('cms_sections', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.D('cms_sections', data)
return data
async def get_visible_sections():
"""获取所有可见栏目(公开接口)"""
sor = _get_db()
ns = {'is_visible': '1', 'sort': 'sort_order asc'}
rows = await sor.R('cms_sections', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = {'is_visible': '1', 'sort': 'sort_order asc'}
rows = await sor.R('cms_sections', ns)
import json as _json
for r in rows:
for field in ['display_config', 'style_config', 'static_content']:
@ -169,39 +183,44 @@ async def get_visible_sections():
# ===== CMS Site Config CRUD =====
async def cms_site_config_list(ns=None):
sor = _get_db()
ns = ns or {}
ns.setdefault('sort', 'config_group asc, sort_order asc')
rows = await sor.R('cms_site_config', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = ns or {}
ns.setdefault('sort', 'config_group asc, sort_order asc')
rows = await sor.R('cms_site_config', ns)
return {'rows': rows, 'total': len(rows)}
async def cms_site_config_create(data):
sor = _get_db()
data['id'] = getID()
await sor.C('cms_site_config', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
data['id'] = getID()
await sor.C('cms_site_config', data)
return data
async def cms_site_config_update(data):
sor = _get_db()
await sor.U('cms_site_config', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.U('cms_site_config', data)
return data
async def cms_site_config_delete(data):
sor = _get_db()
await sor.D('cms_site_config', data)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
await sor.D('cms_site_config', data)
return data
async def get_site_config(group=None):
"""获取站点配置(公开接口)"""
sor = _get_db()
ns = {'sort': 'sort_order asc'}
if group:
ns['config_group'] = group
rows = await sor.R('cms_site_config', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = {'sort': 'sort_order asc'}
if group:
ns['config_group'] = group
rows = await sor.R('cms_site_config', ns)
result = {}
for r in rows:
g = r.get('config_group', '')
@ -214,11 +233,12 @@ async def get_site_config(group=None):
# ===== Public Content APIs =====
async def get_published_content(content_type=None, limit=10):
"""获取已发布内容(公开接口)"""
sor = _get_db()
ns = {'status': 'published', 'sort': 'sort_order asc, published_at desc'}
if content_type:
ns['content_type'] = content_type
rows = await sor.R('cms_content', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = {'status': 'published', 'sort': 'sort_order asc, published_at desc'}
if content_type:
ns['content_type'] = content_type
rows = await sor.R('cms_content', ns)
if limit:
rows = rows[:limit]
return rows
@ -226,36 +246,39 @@ async def get_published_content(content_type=None, limit=10):
async def get_latest_news(limit=2):
"""获取最新新闻(公开接口)"""
sor = _get_db()
ns = {'status': 'published', 'content_type': 'news', 'sort': 'published_at desc'}
rows = await sor.R('cms_content', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = {'status': 'published', 'content_type': 'news', 'sort': 'published_at desc'}
rows = await sor.R('cms_content', ns)
return rows[:limit]
async def get_content_detail(content_id):
"""获取内容详情(公开接口)"""
sor = _get_db()
ns = {'id': content_id, 'status': 'published'}
rows = await sor.R('cms_content', ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
ns = {'id': content_id, 'status': 'published'}
rows = await sor.R('cms_content', ns)
return rows[0] if rows else None
# ===== Submit for approval =====
async def submit_content_for_approval(content_id, title, applicant_id):
"""提交内容审批调用dingdingflow"""
# 更新内容状态为pending
sor = _get_db()
await sor.U('cms_content', {'id': content_id, 'status': 'pending'})
# 调用dingdingflow的submit_approval
try:
from dingdingflow.init import submit_approval
result = await submit_approval('content_publish', content_id, title, applicant_id)
# 保存审批ID
if result and result.get('approval_id'):
await sor.U('cms_content', {'id': content_id, 'approval_id': result['approval_id']})
return result
except ImportError:
return {'status': 'error', 'message': 'dingdingflow模块未安装'}
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
# 更新内容状态为pending
await sor.U('cms_content', {'id': content_id, 'status': 'pending'})
# 调用dingdingflow的submit_approval
try:
from dingdingflow.init import submit_approval
result = await submit_approval('content_publish', content_id, title, applicant_id)
# 保存审批ID
if result and result.get('approval_id'):
await sor.U('cms_content', {'id': content_id, 'approval_id': result['approval_id']})
return result
except ImportError:
return {'status': 'error', 'message': 'dingdingflow模块未安装'}
def load_entcms():