#!/usr/bin/env python3 # -*- coding: utf-8 -*- """短信生态配置写入脚本(迁移自 PineSoundServer .env)。 把阿里云凭据/签名/区域与 PineSound 短信模板(仅本项目需要的 T1 登录)写入 system_configs 表: - sms.provider -> aliyun - sms.aliyun -> 凭据/签名/区域(与 PineSoundServer .env 完全一致: ACCESS_KEY_ID / ACCESS_KEY_SECRET / REGION_ID=cn-qingdao / SIGN_NAME=派音人工智能) - sms.template.login -> SMS_510980133(PineSound T1 登录验证码,变量 code) - sms.template.register -> SMS_510980133(本项目注册=手机号登录一体,复用 T1) PineSound 其余模板(修改密码/绑定手机/找回密码/注销/充值/内测)本项目当前业务 未使用,不迁移;notification / order_status / appointment 场景 PineSound 无匹配 模板,保持留空(stub 打印,不阻断业务)。 用法: python scripts/db/set_sms_config.py # 实际执行 python scripts/db/set_sms_config.py --dry-run # 只预览不写库 """ from __future__ import annotations import argparse import asyncio from pathlib import Path from dotenv import load_dotenv load_dotenv(Path(__file__).resolve().parent.parent.parent / ".env") from sqlalchemy import text # noqa: E402 from app.infrastructure.db import AsyncSessionLocal # noqa: E402 from app.infrastructure.repositories import utcnow_iso # noqa: E402 # 与 PineSoundServer .env 完全一致的阿里云短信配置 ALIYUN_CONFIG = { "access_key_id": "LTAI5t6ZU2Nf8qbGpEpn7bzN", "access_key_secret": "lnNkxiXvNVsCPdc7hVZSXz6goOz8YJ", "sign_name": "派音人工智能", "region_id": "cn-qingdao", "endpoint": "dysmsapi.aliyuncs.com", } # key -> (value, description);幂等覆盖:已存在的键也更新为 PineSound 配置。 SMS_CONFIGS: list[tuple[str, str, str]] = [ ("sms.provider", "aliyun", "短信 provider(stub|aliyun)"), ("sms.aliyun", __import__("json").dumps(ALIYUN_CONFIG, ensure_ascii=False), "阿里云短信总配置(与 PineSoundServer .env 一致)"), ("sms.template.login", '{"template_id":"SMS_510980133","variables":["code"]}', "登录验证码模板(PineSound T1:验证码 code,5分钟有效,请勿泄露)"), ("sms.template.register", '{"template_id":"SMS_510980133","variables":["code"]}', "注册验证码模板(本项目注册=手机号登录一体,复用 T1)"), ("sms.template.notification", '{"template_id":"","variables":[]}', "通知提醒模板(PineSound 无匹配模板,暂留空)"), ("sms.template.order_status", '{"template_id":"","variables":["order_no","status"]}', "订单状态变动模板(PineSound 无匹配模板,暂留空)"), ("sms.template.appointment", '{"template_id":"","variables":["service_name","schedule_at","contact"]}', "服务预约模板(PineSound 无匹配模板,暂留空)"), ] async def main(dry_run: bool) -> int: now = utcnow_iso() async with AsyncSessionLocal() as session: for key, value, description in SMS_CONFIGS: exists = ( await session.execute( text("SELECT `key` FROM system_configs WHERE `key` = :k"), {"k": key}, ) ).scalar_one_or_none() if exists: sql = text( "UPDATE system_configs SET `value` = :v, description = :d, " "updated_at = :now WHERE `key` = :k" ) else: sql = text( "INSERT INTO system_configs (`key`, `value`, `description`, " "updated_at) VALUES (:k, :v, :d, :now)" ) if dry_run: print(f"[dry-run] {'UPDATE' if exists else 'INSERT'} {key} = {value}") continue await session.execute(sql, {"k": key, "v": value, "d": description, "now": now}) if not dry_run: await session.commit() return 0 if __name__ == "__main__": parser = argparse.ArgumentParser(description="写入短信生态配置(PineSound 迁移)") parser.add_argument("--dry-run", action="store_true", help="只预览不写库") args = parser.parse_args() raise SystemExit(asyncio.run(main(args.dry_run)))