diff --git a/README.md b/README.md index 5a6ed1a..60e5df8 100644 --- a/README.md +++ b/README.md @@ -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+ diff --git a/scripts/import_init_smssend.py b/scripts/import_init_smssend.py new file mode 100644 index 0000000..644d011 --- /dev/null +++ b/scripts/import_init_smssend.py @@ -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 && 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()