feat: 微信通道(机构级公众号配置+用户openid绑定+回调框架)+修复mysql.ddl.sql的xls2ui双重引号bug
This commit is contained in:
parent
c29f676cb3
commit
4a9eb227df
25
models/wechat_channel_config.json
Normal file
25
models/wechat_channel_config.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "wechat_channel_config",
|
||||
"title": "微信通道配置(机构级公众号)",
|
||||
"primary": ["id"],
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "org_id", "title": "机构ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "name", "title": "公众号名称", "type": "str", "length": 200, "nullable": "no"},
|
||||
{"name": "appid", "title": "公众号AppID", "type": "str", "length": 100, "nullable": "no"},
|
||||
{"name": "appsecret", "title": "公众号AppSecret(加密)", "type": "str", "length": 500, "nullable": "no"},
|
||||
{"name": "token", "title": "消息校验Token", "type": "str", "length": 200, "nullable": "no"},
|
||||
{"name": "encoding_aes_key", "title": "消息加密Key(可选)", "type": "str", "length": 200},
|
||||
{"name": "enabled", "title": "是否启用", "type": "str", "length": 1, "nullable": "no"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "timestamp"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_wcc_org", "idxtype": "unique", "idxfields": ["org_id"]}
|
||||
]
|
||||
}
|
||||
21
models/wechat_user_binding.json
Normal file
21
models/wechat_user_binding.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "wechat_user_binding",
|
||||
"title": "微信用户绑定(openid→系统用户)",
|
||||
"primary": ["id"],
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "openid", "title": "微信OpenID", "type": "str", "length": 100, "nullable": "no"},
|
||||
{"name": "user_id", "title": "系统用户ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "org_id", "title": "机构ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_wub_openid", "idxtype": "unique", "idxfields": ["openid"]},
|
||||
{"name": "idx_wub_user", "idxtype": "normal", "idxfields": ["user_id"]}
|
||||
]
|
||||
}
|
||||
@ -287,3 +287,47 @@ CREATE INDEX pipeline_agent_questions_idx_paq_tenant ON pipeline_agent_question
|
||||
CREATE INDEX pipeline_agent_questions_idx_paq_task ON pipeline_agent_questions(task_id);
|
||||
CREATE INDEX pipeline_agent_questions_idx_paq_status ON pipeline_agent_questions(status);
|
||||
|
||||
|
||||
-- models/wechat_channel_config.json
|
||||
drop table if exists wechat_channel_config;
|
||||
CREATE TABLE wechat_channel_config
|
||||
(
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`org_id` VARCHAR(32) NOT NULL comment '机构ID',
|
||||
`name` VARCHAR(200) NOT NULL comment '公众号名称',
|
||||
`appid` VARCHAR(100) NOT NULL comment '公众号AppID',
|
||||
`appsecret` VARCHAR(500) NOT NULL comment '公众号AppSecret(加密)',
|
||||
`token` VARCHAR(200) NOT NULL comment '消息校验Token',
|
||||
`encoding_aes_key` VARCHAR(200) comment '消息加密Key(可选)',
|
||||
`enabled` VARCHAR(1) NOT NULL DEFAULT '1' comment '是否启用(1/0)',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间',
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '更新时间'
|
||||
,primary key(id)
|
||||
)
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci
|
||||
engine=innodb
|
||||
comment '微信通道配置(机构级公众号)'
|
||||
;
|
||||
CREATE UNIQUE INDEX wechat_channel_config_idx_wcc_org ON wechat_channel_config(org_id);
|
||||
|
||||
|
||||
-- models/wechat_user_binding.json
|
||||
drop table if exists wechat_user_binding;
|
||||
CREATE TABLE wechat_user_binding
|
||||
(
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`openid` VARCHAR(100) NOT NULL comment '微信OpenID',
|
||||
`user_id` VARCHAR(32) NOT NULL comment '系统用户ID',
|
||||
`org_id` VARCHAR(32) NOT NULL comment '机构ID',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间'
|
||||
,primary key(id)
|
||||
)
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci
|
||||
engine=innodb
|
||||
comment '微信用户绑定(openid→系统用户)'
|
||||
;
|
||||
CREATE UNIQUE INDEX wechat_user_binding_idx_wub_openid ON wechat_user_binding(openid);
|
||||
CREATE INDEX wechat_user_binding_idx_wub_user ON wechat_user_binding(user_id);
|
||||
|
||||
|
||||
57
scripts/fix_wechat_ddl.sql
Normal file
57
scripts/fix_wechat_ddl.sql
Normal file
@ -0,0 +1,57 @@
|
||||
-- 修复脚本:重建被误删的 sd_deploy_envs + 新建微信通道两张表
|
||||
-- 注意:原 mysql.ddl.sql 有 xls2ui 生成 bug(DEFAULT ''N'' 双重引号),不能整文件执行
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sd_deploy_envs (
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`project_id` VARCHAR(32) NOT NULL comment '项目ID',
|
||||
`env_type` VARCHAR(20) NOT NULL comment '环境类型',
|
||||
`host` VARCHAR(200) NOT NULL comment 'SSH主机地址',
|
||||
`port` int NOT NULL DEFAULT 22 comment 'SSH端口',
|
||||
`user` VARCHAR(100) NOT NULL comment 'SSH用户',
|
||||
`ssh_key_path` VARCHAR(500) comment 'SSH密钥路径',
|
||||
`sudo_enabled` VARCHAR(1) NOT NULL DEFAULT 'N' comment '免密Sudo',
|
||||
`deploy_path` VARCHAR(500) NOT NULL comment '部署目录',
|
||||
`python_path` VARCHAR(500) comment 'Python路径',
|
||||
`db_host` VARCHAR(200) comment '数据库地址',
|
||||
`db_port` int DEFAULT 3306 comment '数据库端口',
|
||||
`db_name` VARCHAR(100) comment '数据库名',
|
||||
`db_user` VARCHAR(100) comment '数据库用户',
|
||||
`db_password` VARCHAR(500) comment '数据库密码(加密)',
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT 'configured' comment '环境状态',
|
||||
`verified_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '最近验证时间',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间',
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '更新时间',
|
||||
primary key(id)
|
||||
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci engine=innodb comment '部署环境表';
|
||||
|
||||
CREATE INDEX idx_sd_deploy_envs_project ON sd_deploy_envs(project_id);
|
||||
CREATE INDEX idx_sd_deploy_envs_type ON sd_deploy_envs(env_type);
|
||||
CREATE UNIQUE INDEX idx_sd_deploy_envs_unique ON sd_deploy_envs(project_id, env_type);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wechat_channel_config (
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`org_id` VARCHAR(32) NOT NULL comment '机构ID',
|
||||
`name` VARCHAR(200) NOT NULL comment '公众号名称',
|
||||
`appid` VARCHAR(100) NOT NULL comment '公众号AppID',
|
||||
`appsecret` VARCHAR(500) NOT NULL comment '公众号AppSecret(加密)',
|
||||
`token` VARCHAR(200) NOT NULL comment '消息校验Token',
|
||||
`encoding_aes_key` VARCHAR(200) comment '消息加密Key(可选)',
|
||||
`enabled` VARCHAR(1) NOT NULL DEFAULT '1' comment '是否启用(1/0)',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间',
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '更新时间',
|
||||
primary key(id)
|
||||
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci engine=innodb comment '微信通道配置(机构级公众号)';
|
||||
|
||||
CREATE UNIQUE INDEX wechat_channel_config_idx_wcc_org ON wechat_channel_config(org_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wechat_user_binding (
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`openid` VARCHAR(100) NOT NULL comment '微信OpenID',
|
||||
`user_id` VARCHAR(32) NOT NULL comment '系统用户ID',
|
||||
`org_id` VARCHAR(32) NOT NULL comment '机构ID',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间',
|
||||
primary key(id)
|
||||
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci engine=innodb comment '微信用户绑定(openid→系统用户)';
|
||||
|
||||
CREATE UNIQUE INDEX wechat_user_binding_idx_wub_openid ON wechat_user_binding(openid);
|
||||
CREATE INDEX wechat_user_binding_idx_wub_user ON wechat_user_binding(user_id);
|
||||
61
wwwroot/api/wechat_binding.dspy
Normal file
61
wwwroot/api/wechat_binding.dspy
Normal file
@ -0,0 +1,61 @@
|
||||
# wechat_binding.dspy - 微信用户绑定(openid → 系统用户)
|
||||
# action=get: 查当前用户绑定状态
|
||||
# action=bind: 绑定自己 openid
|
||||
# action=unbind: 解绑
|
||||
|
||||
import json
|
||||
|
||||
uid = await get_user()
|
||||
if not uid:
|
||||
return json.dumps({"success": False, "error": "请先登录"}, ensure_ascii=False)
|
||||
|
||||
action = (params_kw or {}).get('action', 'get')
|
||||
dbname = get_module_dbname('pipeline-sdlc')
|
||||
|
||||
# 查用户 org_id
|
||||
org_id = ''
|
||||
try:
|
||||
async with DBPools().sqlorContext('sage') as sor:
|
||||
urecs = await sor.sqlExe("SELECT orgid FROM users WHERE id=${u}$", {"u": uid})
|
||||
if urecs:
|
||||
org_id = getattr(urecs[0], 'orgid', '') or ''
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if action == 'get':
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT openid FROM wechat_user_binding WHERE user_id=${u}$", {"u": uid})
|
||||
bound = getattr(recs[0], 'openid', '') if recs else ''
|
||||
return json.dumps({"success": True, "bound": bool(bound), "openid": bound}, ensure_ascii=False)
|
||||
|
||||
elif action == 'bind':
|
||||
openid = (params_kw or {}).get('openid', '').strip()
|
||||
if not openid:
|
||||
return json.dumps({"success": False, "error": "openid 必填"}, ensure_ascii=False)
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
# openid 唯一:检查是否已被其他账号绑定
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT user_id FROM wechat_user_binding WHERE openid=${o}$", {"o": openid})
|
||||
if recs and getattr(recs[0], 'user_id', '') != uid:
|
||||
return json.dumps({"success": False, "error": "该微信已绑定其他账号"}, ensure_ascii=False)
|
||||
existing = await sor.sqlExe(
|
||||
"SELECT id FROM wechat_user_binding WHERE user_id=${u}$", {"u": uid})
|
||||
if existing:
|
||||
await sor.sqlExe(
|
||||
"UPDATE wechat_user_binding SET openid=${o}$ WHERE user_id=${u}$",
|
||||
{"o": openid, "u": uid})
|
||||
else:
|
||||
await sor.C('wechat_user_binding', {
|
||||
'id': getID(), 'openid': openid, 'user_id': uid, 'org_id': org_id,
|
||||
})
|
||||
return json.dumps({"success": True}, ensure_ascii=False)
|
||||
|
||||
elif action == 'unbind':
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.sqlExe(
|
||||
"DELETE FROM wechat_user_binding WHERE user_id=${u}$", {"u": uid})
|
||||
return json.dumps({"success": True}, ensure_ascii=False)
|
||||
|
||||
else:
|
||||
return json.dumps({"error": "Unknown action: " + str(action)}, ensure_ascii=False)
|
||||
125
wwwroot/api/wechat_callback.dspy
Normal file
125
wwwroot/api/wechat_callback.dspy
Normal file
@ -0,0 +1,125 @@
|
||||
# wechat_callback.dspy - 微信回调(验签 + 消息接收 + openid映射 + gateway路由 + 被动回复)
|
||||
# GET: 服务器配置验证(验签返回 echostr)
|
||||
# POST: 接收消息 → openid 映射 user_id → gateway.run_message → 回复 XML
|
||||
|
||||
import json
|
||||
import hashlib
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
signature = (params_kw or {}).get('signature', '')
|
||||
timestamp = (params_kw or {}).get('timestamp', '')
|
||||
nonce = (params_kw or {}).get('nonce', '')
|
||||
echostr = (params_kw or {}).get('echostr', '')
|
||||
|
||||
dbname = get_module_dbname('pipeline-sdlc')
|
||||
|
||||
|
||||
async def _get_channel_config(sor):
|
||||
"""取启用的机构公众号配置(简化:取第一条启用配置;多机构时按 ToUserName 区分)。"""
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT org_id, appid, token, encoding_aes_key FROM wechat_channel_config "
|
||||
"WHERE enabled='1' LIMIT 1", {})
|
||||
if recs:
|
||||
r = recs[0]
|
||||
return {
|
||||
'org_id': getattr(r, 'org_id', ''),
|
||||
'appid': getattr(r, 'appid', ''),
|
||||
'token': getattr(r, 'token', ''),
|
||||
'encoding_aes_key': getattr(r, 'encoding_aes_key', ''),
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
async def _resolve_user(sor, openid):
|
||||
"""openid → user_id(未绑定返回空)。"""
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT user_id FROM wechat_user_binding WHERE openid=${o}$", {"o": openid})
|
||||
return getattr(recs[0], 'user_id', '') if recs else ''
|
||||
|
||||
|
||||
# ── GET 验签 ──
|
||||
if echostr:
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
cfg = await _get_channel_config(sor)
|
||||
if not cfg:
|
||||
return "未配置微信通道"
|
||||
tmp = sorted([cfg['token'], timestamp, nonce])
|
||||
sha1 = hashlib.sha1(''.join(tmp).encode('utf-8')).hexdigest()
|
||||
if sha1 == signature:
|
||||
return echostr
|
||||
return "验签失败"
|
||||
|
||||
|
||||
# ── POST 消息处理 ──
|
||||
# 微信消息 XML 在 request body。Sage DSPY 环境:优先取 params_kw 里的 xml,否则取 request body。
|
||||
xml_body = (params_kw or {}).get('xml', '')
|
||||
if not xml_body:
|
||||
try:
|
||||
xml_body = await request.text() if hasattr(request, 'text') else ''
|
||||
except Exception:
|
||||
xml_body = ''
|
||||
if not xml_body:
|
||||
try:
|
||||
xml_body = (await request.read()).decode('utf-8') if hasattr(request, 'read') else ''
|
||||
except Exception:
|
||||
xml_body = ''
|
||||
|
||||
if not xml_body:
|
||||
return ""
|
||||
|
||||
try:
|
||||
root = ET.fromstring(xml_body)
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def _x(node, name):
|
||||
e = node.find(name)
|
||||
return e.text if e is not None else ''
|
||||
|
||||
to_user = _x(root, 'ToUserName') # 公众号(机构)
|
||||
from_user = _x(root, 'FromUserName') # openid
|
||||
msg_type = _x(root, 'MsgType')
|
||||
content = _x(root, 'Content')
|
||||
|
||||
if msg_type != 'text' or not from_user:
|
||||
# 非文本消息,回复统一提示
|
||||
reply_text = "暂只支持文本消息"
|
||||
else:
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
cfg = await _get_channel_config(sor)
|
||||
user_id = await _resolve_user(sor, from_user)
|
||||
|
||||
if not user_id:
|
||||
reply_text = "未绑定账号,请先登录系统在「微信绑定」中绑定您的微信"
|
||||
else:
|
||||
# 走 gateway 统一入口(微信通道)
|
||||
from pipeline_service.gateway import get_gateway
|
||||
gw = get_gateway()
|
||||
parts = []
|
||||
try:
|
||||
async for chunk in gw.run_message("wechat", user_id, content):
|
||||
d = json.loads(chunk)
|
||||
t = d.get('type', '')
|
||||
if t == 'reply':
|
||||
parts.append(d.get('message', ''))
|
||||
elif t == 'ask_user':
|
||||
parts.append("❓ " + d.get('message', ''))
|
||||
elif t == 'error':
|
||||
parts.append("⚠️ " + d.get('message', ''))
|
||||
# tool_call/tool_result 不推送给微信(避免刷屏)
|
||||
except Exception as e:
|
||||
parts.append(f"ERROR: {str(e)[:200]}")
|
||||
reply_text = "\n".join(parts) if parts else "(无回复)"
|
||||
|
||||
# ── 被动回复 XML ──
|
||||
reply_xml = (
|
||||
"<xml>"
|
||||
f"<ToUserName><![CDATA[{from_user}]]></ToUserName>"
|
||||
f"<FromUserName><![CDATA[{to_user}]]></FromUserName>"
|
||||
f"<CreateTime>{int(time.time())}</CreateTime>"
|
||||
"<MsgType><![CDATA[text]]></MsgType>"
|
||||
f"<Content><![CDATA[{reply_text}]]></Content>"
|
||||
"</xml>"
|
||||
)
|
||||
return reply_xml
|
||||
93
wwwroot/api/wechat_config.dspy
Normal file
93
wwwroot/api/wechat_config.dspy
Normal file
@ -0,0 +1,93 @@
|
||||
# wechat_config.dspy - 微信通道配置(机构级公众号,机构管理员)
|
||||
# action=get: 返回当前机构公众号配置(appsecret 脱敏)
|
||||
# action=save: 保存配置(仅机构管理员)
|
||||
|
||||
import json
|
||||
from appPublic.rc4 import password as _enc, unpassword as _dec
|
||||
|
||||
uid = await get_user()
|
||||
if not uid:
|
||||
return json.dumps({"success": False, "error": "请先登录"}, ensure_ascii=False)
|
||||
|
||||
action = (params_kw or {}).get('action', 'get')
|
||||
dbname = get_module_dbname('pipeline-sdlc')
|
||||
|
||||
# 查用户 org_id + 角色(sage 库)
|
||||
org_id = ''
|
||||
is_admin = False
|
||||
try:
|
||||
async with DBPools().sqlorContext('sage') as sor:
|
||||
urecs = await sor.sqlExe("SELECT orgid FROM users WHERE id=${u}$", {"u": uid})
|
||||
if urecs:
|
||||
org_id = getattr(urecs[0], 'orgid', '') or ''
|
||||
rrecs = await sor.sqlExe(
|
||||
"SELECT r.name FROM userrole ur JOIN role r ON ur.roleid=r.id WHERE ur.userid=${u}$",
|
||||
{"u": uid})
|
||||
roles = [getattr(r, 'name', '') for r in (rrecs or [])]
|
||||
is_admin = 'admin' in roles
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if action == 'get':
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT * FROM wechat_channel_config WHERE org_id=${o}$", {"o": org_id})
|
||||
if not recs:
|
||||
return json.dumps({"success": True, "config": None}, ensure_ascii=False)
|
||||
r = recs[0]
|
||||
return json.dumps({"success": True, "is_admin": is_admin, "config": {
|
||||
"id": getattr(r, 'id', ''),
|
||||
"name": getattr(r, 'name', ''),
|
||||
"appid": getattr(r, 'appid', ''),
|
||||
"appsecret": "***", # 脱敏,不回传明文
|
||||
"token": getattr(r, 'token', ''),
|
||||
"encoding_aes_key": getattr(r, 'encoding_aes_key', ''),
|
||||
"enabled": getattr(r, 'enabled', '1'),
|
||||
}}, ensure_ascii=False)
|
||||
|
||||
elif action == 'save':
|
||||
if not is_admin:
|
||||
return json.dumps({"success": False, "error": "仅机构管理员可配置微信通道"}, ensure_ascii=False)
|
||||
if not org_id:
|
||||
return json.dumps({"success": False, "error": "用户未关联机构"}, ensure_ascii=False)
|
||||
|
||||
name = (params_kw or {}).get('name', '')
|
||||
appid = (params_kw or {}).get('appid', '')
|
||||
appsecret = (params_kw or {}).get('appsecret', '')
|
||||
token = (params_kw or {}).get('token', '')
|
||||
aes_key = (params_kw or {}).get('encoding_aes_key', '')
|
||||
enabled = (params_kw or {}).get('enabled', '1')
|
||||
|
||||
if not appid or not token:
|
||||
return json.dumps({"success": False, "error": "appid/token 必填"}, ensure_ascii=False)
|
||||
# appsecret 为空时表示不修改(保留原值)
|
||||
if not appsecret:
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT appsecret FROM wechat_channel_config WHERE org_id=${o}$", {"o": org_id})
|
||||
if recs:
|
||||
appsecret = _dec(getattr(recs[0], 'appsecret', '') or '')
|
||||
|
||||
if not appsecret:
|
||||
return json.dumps({"success": False, "error": "appsecret 必填"}, ensure_ascii=False)
|
||||
|
||||
enc_secret = _enc(appsecret)
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT id FROM wechat_channel_config WHERE org_id=${o}$", {"o": org_id})
|
||||
if recs:
|
||||
await sor.sqlExe(
|
||||
"UPDATE wechat_channel_config SET name=${n}$, appid=${a}$, appsecret=${s}$, "
|
||||
"token=${t}$, encoding_aes_key=${k}$, enabled=${e}$ WHERE org_id=${o}$",
|
||||
{"n": name, "a": appid, "s": enc_secret, "t": token, "k": aes_key,
|
||||
"e": enabled, "o": org_id})
|
||||
else:
|
||||
await sor.C('wechat_channel_config', {
|
||||
'id': getID(), 'org_id': org_id, 'name': name, 'appid': appid,
|
||||
'appsecret': enc_secret, 'token': token, 'encoding_aes_key': aes_key,
|
||||
'enabled': enabled,
|
||||
})
|
||||
return json.dumps({"success": True}, ensure_ascii=False)
|
||||
|
||||
else:
|
||||
return json.dumps({"error": "Unknown action: " + str(action)}, ensure_ascii=False)
|
||||
Loading…
x
Reference in New Issue
Block a user