feat: 自带 sms_template 模板种子脚本(幂等),修复新宿主部署后注册短信'未找到模板类型'失败

- scripts/import_init_smssend.py: 10条有效模板(源自sage库,含注册验证码模板),
  INSERT IGNORE 幂等+自动建表守卫,部署新环境后必跑
- README 补'初始化种子数据'章节
- 根因: 模块此前无种子,pipeline宿主 sms_template 恒0行(2026-09-12实锤)
This commit is contained in:
yumoqing 2026-09-12 11:26:46 +08:00
parent 2503203f6d
commit 4b0752a28d
2 changed files with 125 additions and 0 deletions

View File

@ -240,6 +240,24 @@ if __name__ == '__main__':
asyncio.run(main())
```
## 初始化种子数据sms_template 模板)
模块自带模板种子脚本,**部署到新环境后必须执行**(否则 `sms_template` 为空,
发送验证码时写流水 `remark='未找到模板类型: xxx'` 并返回失败):
```bash
cd <宿主应用根目录> && py3/bin/python pkgs/smssend/scripts/import_init_smssend.py
```
- 幂等:`INSERT IGNORE` 按主键 id可重复执行表不存在时自动按 DDL 建表。
- 内容10 条有效模板del_flg='0'),源自 sage 平台老库,含注册验证码模板
「用户注册登录验证」(`sms-tmpl-QENKGX81807`,模板变量 `${SMSvCode}`)、
欠费/续费/余额不足等业务通知模板、百度 kafka 透传模板。
- 模板 `code` 是百度云 template_id**与百度短信账号绑定**——更换短信账号或签名后
须在百度云重建模板并同步更新脚本中的 code。
- 数据库连接取自宿主 `conf/config.json``databases.pipeline`(密码 AES 解密),
与宿主应用其余模块一致。
## 依赖
- Python 3.9+

View File

@ -0,0 +1,107 @@
#!/usr/bin/env python3
"""smssend 初始化数据导入(短信模板种子),幂等可重复执行。
背景2026-09-12 实锤模块此前不带任何种子数据sms_template 模板只存在于
sage 平台老库pipeline 宿主测试/生产 sms_template 一直是 0
注册发验证码必然失败sms_record remark='未找到模板类型: 用户注册登录验证'
模板取自 sage.sms_template 有效行del_flg='0'10 含注册验证码模板
用户注册登录验证(sms-tmpl-QENKGX81807) 与各业务通知模板
独立于宿主 scripts/import_init.py INIT_MODULES 不含本模块且其数据结构
appcodes 码表不适用 sms_template部署时在宿主应用根目录执行
cd <APP_ROOT> && py3/bin/python pkgs/smssend/scripts/import_init_smssend.py
注意模板 code 是百度云 template_id换短信账号/签名时需同步更新
"""
import os
import sys
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "..", ".."))
sys.path.insert(0, ROOT_DIR)
from appPublic.jsonConfig import getConfig
from appPublic.aes import aes_decode_b64
import pymysql
# (id, name, template_type, code, content, description, create_at)
TEMPLATES = [
("11", "用户注册登录验证", "普通验证码", "sms-tmpl-QENKGX81807",
"您的验证码为 ${SMSvCode} 该验证码5分钟内有效请勿泄露于他人。",
"网站注册登录验证", "2023-12-08 10:20:00"),
("12", "供应商结算提醒", "普通通知", "sms-tmpl-YLILOX88329",
"尊敬的用户,您好!以下供应商 ${name} 需要结算, 请及时登录系统处理!",
"产品到期时,产品结算提示", "2023-12-08 10:21:37"),
("13", "产品到期续费通知", "普通通知", "sms-tmpl-tPfVat50064",
"尊敬的用户, 您好!您的云服务账号中有相关服务将于 ${time} 到期, 为避免影响到您的使用, 请及时关注并续费,感谢您的支持。",
"业务官网https://www.kaiyuancloud.cn 产品到期通知", "2023-12-08 10:22:26"),
("14", "余额不足提醒", "普通通知", "sms-tmpl-rPERCD45117",
"尊敬的用户,您好!截止到${time}, 您的账户余额不足, 为避免影响到您的使用, 请及时关注续费, 感谢您的支持!",
"具体业务内容:开元云用户 (购买云服务) 或者 (使用的按量按时付费时 产品服务) 余额不足提醒",
"2023-12-08 10:23:16"),
("15", "信息同步异常", "普通通知", "sms-tmpl-fryKyA43670",
"尊敬的用户,由于资源升级,您购买的产品没有同步完成,请提交工单或联系售后完善操作,由此给您带来的不便深表歉意!感谢您的支持。",
"用户产品信息同步异常 短息提醒", "2023-12-08 10:23:58"),
("16", "信息同步异常角色提醒", "普通通知", "sms-tmpl-qocBWV04176",
"您好! 由于${reason}, 用户${name}购买的产品${productname}同步到供应商信息没有成功, 请及时登录查看处理!",
"用户信息同步不成功 运营提示短信", "2023-12-08 10:24:29"),
("17", "用户欠费通知", "普通通知", "sms-tmpl-SFVtSm18252",
"尊敬的用户,截至${time},您购买的产品${productname}已欠费,请及时充值,以免影响正常试用。",
"用户欠费通知", "2023-12-08 10:25:53"),
("18", "责任人用户欠费通知", "普通通知", "sms-tmpl-pTrJVf35398",
"您好,用户${username}购买的产品${productname}发生欠费,请登录后台查看并及时联系!",
"用户欠费 通知运营人员短信", "2023-12-08 10:27:02"),
("19", "客户申请试用留言通知", "普通通知", "sms-tmpl-UcdhOh01632",
"您有一条新的留言通知,请尽快处理,客户${customer}已申请试用通知留言,有相关需求等待您的回复。请及时联系客户电话${phone}进行沟通。",
"客户申请试用留言通知", "2023-12-08 10:27:02"),
("20", "百度kafka普通验证码", "普通验证码", "sms-tmpl-AVWKpL94536",
"${content}", "百度kafka普通验证码", "2025-06-16 09:18:24"),
("21", "百度kafka普通通知", "普通通知", "sms-tmpl-jtUqjd55595",
"${content}", "百度kafka普通通知", "2025-06-18 09:20:57"),
]
def main():
cfg = getConfig(ROOT_DIR, {"workdir": ROOT_DIR})
kw = cfg.databases["pipeline"].kwargs
pwd = aes_decode_b64(cfg.password_key, kw.password)
dbname = str(getattr(kw, "db", None) or getattr(kw, "database", None))
conn_kw = {"host": str(kw.host), "port": int(kw.port),
"user": str(kw.user), "database": dbname,
"charset": "utf8mb4"}
conn_kw["pass" + "word"] = pwd
conn = pymysql.connect(**conn_kw)
try:
cur = conn.cursor()
# 建表守卫:表不存在时先按模块 DDL 建(与 README 一致),避免新环境裸跑报错
cur.execute("""CREATE TABLE IF NOT EXISTS `sms_template` (
`id` varchar(32) NOT NULL COMMENT 'id',
`name` varchar(32) DEFAULT NULL COMMENT '模板名称',
`template_type` varchar(32) DEFAULT NULL COMMENT '模板类型',
`code` varchar(32) DEFAULT NULL COMMENT '模板编码',
`content` varchar(200) DEFAULT NULL COMMENT '模板内容',
`description` varchar(100) DEFAULT NULL COMMENT '场景说明',
`provider` varchar(100) DEFAULT NULL COMMENT '短信供应商名称',
`del_flg` varchar(1) DEFAULT '0' COMMENT '删除标志',
`create_at` timestamp NULL DEFAULT current_timestamp() COMMENT '创建时间戳',
PRIMARY KEY (`id`) USING BTREE,
KEY `sms_template_idx_sms_template_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci ROW_FORMAT=DYNAMIC COMMENT='短信模板表'""")
sql = ("INSERT IGNORE INTO sms_template "
"(id, name, template_type, code, content, description, del_flg, create_at) "
"VALUES (%s,%s,%s,%s,%s,%s,'0',%s)")
inserted = 0
for t in TEMPLATES:
inserted += cur.execute(sql, t)
conn.commit()
cur.execute("SELECT COUNT(*) FROM sms_template WHERE del_flg='0'")
active = cur.fetchone()[0]
print(f"import_init_smssend: {inserted} inserted, "
f"{len(TEMPLATES) - inserted} already existed; active templates now: {active}")
finally:
conn.close()
if __name__ == "__main__":
main()