-- ============================================
-- 数据库安装脚本
-- 系统名称：麟泓授权系统
-- 版本：3.1.8
-- 生成时间：2026-04-29
-- ============================================

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- 1. 公告表
-- ----------------------------
DROP TABLE IF EXISTS `announcements`;
CREATE TABLE `announcements` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `content` text NOT NULL,
  `type` enum('user_dashboard','user_register','user_buy','admin') NOT NULL DEFAULT 'user_dashboard',
  `position` enum('popup','banner','sidebar') NOT NULL DEFAULT 'popup',
  `priority` int(11) NOT NULL DEFAULT '0',
  `start_time` datetime DEFAULT NULL,
  `end_time` datetime DEFAULT NULL,
  `show_frequency` tinyint(1) NOT NULL DEFAULT '0',
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公告表';

-- ----------------------------
-- 2. 公告已读记录表
-- ----------------------------
DROP TABLE IF EXISTS `announcement_reads`;
CREATE TABLE `announcement_reads` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `announcement_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `read_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_read` (`announcement_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公告已读记录表';

-- ----------------------------
-- 3. 应用表
-- ----------------------------
DROP TABLE IF EXISTS `apps`;
CREATE TABLE `apps` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `app_key` varchar(64) NOT NULL DEFAULT '',
  `description` text,
  `price` decimal(10,2) NOT NULL,
  `duration_days` int(11) NOT NULL COMMENT '0永久',
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `app_key` (`app_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 4. 余额日志表
-- ----------------------------
DROP TABLE IF EXISTS `balance_logs`;
CREATE TABLE `balance_logs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `amount` decimal(10,2) NOT NULL,
  `type` enum('recharge','consume','refund') NOT NULL,
  `remark` varchar(255) DEFAULT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 5. 卡密表
-- ----------------------------
DROP TABLE IF EXISTS `card_codes`;
CREATE TABLE `card_codes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `card_code` varchar(32) NOT NULL,
  `amount` decimal(10,2) NOT NULL,
  `status` tinyint(4) DEFAULT '0',
  `used_by` int(11) DEFAULT NULL,
  `used_at` datetime DEFAULT NULL,
  `created_by` int(11) DEFAULT NULL,
  `expire_at` datetime DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `card_code` (`card_code`),
  KEY `idx_card_code` (`card_code`),
  KEY `idx_status` (`status`),
  KEY `idx_used_by` (`used_by`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='卡密表';

-- ----------------------------
-- 6. 更新日志表
-- ----------------------------
DROP TABLE IF EXISTS `changelog`;
CREATE TABLE `changelog` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `version` varchar(20) NOT NULL,
  `release_date` date NOT NULL,
  `content` text NOT NULL,
  `is_featured` tinyint(1) NOT NULL DEFAULT '0',
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `version` (`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='更新日志表';

-- ----------------------------
-- 7. 资源下载表
-- ----------------------------
DROP TABLE IF EXISTS `downloads`;
CREATE TABLE `downloads` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `description` text,
  `file_name` varchar(255) NOT NULL,
  `file_path` varchar(500) NOT NULL,
  `file_size` varchar(50) DEFAULT NULL,
  `download_count` int(11) NOT NULL DEFAULT '0',
  `version` varchar(50) DEFAULT NULL,
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `sort` int(11) NOT NULL DEFAULT '0',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资源下载表';

-- ----------------------------
-- 8. 邮件发送日志表
-- ----------------------------
DROP TABLE IF EXISTS `email_log`;
CREATE TABLE `email_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `to_email` varchar(100) NOT NULL,
  `subject` varchar(255) NOT NULL,
  `content` text,
  `status` tinyint(4) DEFAULT '0',
  `error_msg` varchar(255) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_to_email` (`to_email`),
  KEY `idx_created` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='邮件发送日志表';

-- ----------------------------
-- 9. 邮件模板表
-- ----------------------------
DROP TABLE IF EXISTS `email_templates`;
CREATE TABLE `email_templates` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(50) NOT NULL,
  `name` varchar(100) NOT NULL,
  `subject` varchar(255) NOT NULL,
  `content` text NOT NULL,
  `variables` text,
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `payment_methods` text,
  PRIMARY KEY (`id`),
  UNIQUE KEY `type` (`type`),
  KEY `idx_type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='邮件模板表';

-- 邮件模板初始数据
INSERT IGNORE INTO `email_templates` (`id`, `type`, `name`, `subject`, `content`, `variables`, `updated_at`, `payment_methods`) VALUES
(1, 'recharge', '充值成功通知', '【{site_name}】充值成功通知', '<!DOCTYPE html>\r\n<html>\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <style>\r\n        * {\r\n            margin: 0;\r\n            padding: 0;\r\n            box-sizing: border-box;\r\n        }\r\n        body {\r\n            font-family: -apple-system, BlinkMacSystemFont, ''Segoe UI'', Roboto, ''Helvetica Neue'', Arial, sans-serif;\r\n            background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);\r\n            padding: 40px 20px;\r\n            min-height: 100vh;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n        }\r\n        .card {\r\n            max-width: 480px;\r\n            margin: 0 auto;\r\n            background: #ffffff;\r\n            border-radius: 24px;\r\n            overflow: hidden;\r\n            box-shadow: 0 20px 35px -8px rgba(16, 185, 129, 0.2);\r\n        }\r\n        .header {\r\n            background: linear-gradient(135deg, #10b981 0%, #059669 100%);\r\n            padding: 36px 30px;\r\n            text-align: center;\r\n            position: relative;\r\n            overflow: hidden;\r\n        }\r\n        .header::before {\r\n            content: '''';\r\n            position: absolute;\r\n            top: -50%;\r\n            right: -20%;\r\n            width: 200px;\r\n            height: 200px;\r\n            background: rgba(255, 255, 255, 0.08);\r\n            border-radius: 50%;\r\n        }\r\n        .header::after {\r\n            content: '''';\r\n            position: absolute;\r\n            bottom: -30%;\r\n            left: -10%;\r\n            width: 150px;\r\n            height: 150px;\r\n            background: rgba(255, 255, 255, 0.05);\r\n            border-radius: 50%;\r\n        }\r\n        .header-icon {\r\n            width: 64px;\r\n            height: 64px;\r\n            background: rgba(255, 255, 255, 0.2);\r\n            border-radius: 50%;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin: 0 auto 16px;\r\n            position: relative;\r\n            z-index: 1;\r\n        }\r\n        .header-icon svg {\r\n            width: 36px;\r\n            height: 36px;\r\n            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\r\n        }\r\n        .header h2 {\r\n            color: white;\r\n            margin: 0;\r\n            font-size: 26px;\r\n            font-weight: 700;\r\n            letter-spacing: 0.5px;\r\n            position: relative;\r\n            z-index: 1;\r\n            text-shadow: 0 2px 4px rgba(0,0,0,0.1);\r\n        }\r\n        .content {\r\n            padding: 36px 30px;\r\n        }\r\n        .greeting {\r\n            font-size: 16px;\r\n            color: #1f2937;\r\n            margin-bottom: 24px;\r\n            line-height: 1.6;\r\n        }\r\n        .greeting strong {\r\n            color: #059669;\r\n            font-weight: 600;\r\n        }\r\n        .amount-box {\r\n            background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);\r\n            border-radius: 20px;\r\n            padding: 28px 20px;\r\n            text-align: center;\r\n            margin-bottom: 28px;\r\n            border: 1px solid rgba(16, 185, 129, 0.15);\r\n        }\r\n        .amount-label {\r\n            font-size: 14px;\r\n            color: #6b7280;\r\n            margin-bottom: 10px;\r\n            text-transform: uppercase;\r\n            letter-spacing: 1px;\r\n        }\r\n        .amount-value {\r\n            font-size: 48px;\r\n            font-weight: 800;\r\n            color: #059669;\r\n            line-height: 1.2;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 4px;\r\n        }\r\n        .amount-value .currency {\r\n            font-size: 28px;\r\n            font-weight: 600;\r\n        }\r\n        .info-row {\r\n            display: flex;\r\n            align-items: center;\r\n            padding: 12px 0;\r\n            border-bottom: 1px solid #f3f4f6;\r\n        }\r\n        .info-row:last-child {\r\n            border-bottom: none;\r\n        }\r\n        .info-icon {\r\n            width: 32px;\r\n            height: 32px;\r\n            background: #f3f4f6;\r\n            border-radius: 10px;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin-right: 14px;\r\n        }\r\n        .info-icon svg {\r\n            width: 18px;\r\n            height: 18px;\r\n            color: #6b7280;\r\n        }\r\n        .info-content {\r\n            flex: 1;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: space-between;\r\n        }\r\n        .info-label {\r\n            font-size: 14px;\r\n            color: #6b7280;\r\n        }\r\n        .info-value {\r\n            font-size: 15px;\r\n            font-weight: 500;\r\n            color: #1f2937;\r\n        }\r\n        .balance-value {\r\n            font-size: 20px;\r\n            font-weight: 700;\r\n            color: #10b981;\r\n        }\r\n        .divider {\r\n            margin: 28px 0 20px;\r\n            border: none;\r\n            border-top: 1px solid #e5e7eb;\r\n        }\r\n        .footer {\r\n            text-align: center;\r\n            color: #9ca3af;\r\n            font-size: 12px;\r\n            padding: 20px 30px 30px;\r\n            border-top: 1px solid #f3f4f6;\r\n            background: #fafafa;\r\n        }\r\n        .footer p {\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 6px;\r\n        }\r\n        .btn {\r\n            display: inline-block;\r\n            background: linear-gradient(135deg, #10b981 0%, #059669 100%);\r\n            color: white;\r\n            text-decoration: none;\r\n            padding: 14px 32px;\r\n            border-radius: 40px;\r\n            font-weight: 600;\r\n            font-size: 15px;\r\n            margin-top: 20px;\r\n            box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);\r\n            transition: all 0.2s;\r\n        }\r\n        .btn:hover {\r\n            transform: translateY(-2px);\r\n            box-shadow: 0 6px 16px rgba(16, 185, 129, 0.4);\r\n        }\r\n        @media (max-width: 480px) {\r\n            body {\r\n                padding: 20px 12px;\r\n            }\r\n            .header {\r\n                padding: 28px 20px;\r\n            }\r\n            .content {\r\n                padding: 28px 20px;\r\n            }\r\n            .amount-value {\r\n                font-size: 40px;\r\n            }\r\n        }\r\n    </style>\r\n</head>\r\n<body>\r\n    <div class=\"card\">\r\n        <div class=\"header\">\r\n            <div class=\"header-icon\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"white\">\r\n                    <path d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"/>\r\n                </svg>\r\n            </div>\r\n            <h2>充值成功</h2>\r\n        </div>\r\n        <div class=\"content\">\r\n            <div class=\"greeting\">\r\n                尊敬的 <strong>{username}</strong>，您好！\r\n            </div>\r\n            \r\n            <div class=\"amount-box\">\r\n                <div class=\"amount-label\">充值金额</div>\r\n                <div class=\"amount-value\">\r\n                    <span class=\"currency\">¥</span>{amount}\r\n                </div>\r\n            </div>\r\n            \r\n            <div class=\"info-row\">\r\n                <div class=\"info-icon\">\r\n                    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                        <path d=\"M20,8H4V6H20V8M20,18H4V12H20V18M20,4H4C2.9,4 2,4.9 2,6V18C2,19.1 2.9,20 4,20H20C21.1,20 22,19.1 22,18V6C22,4.9 21.1,4 20,4Z\"/>\r\n                    </svg>\r\n                </div>\r\n                <div class=\"info-content\">\r\n                    <span class=\"info-label\">订单号</span>\r\n                    <span class=\"info-value\">{order_no}</span>\r\n                </div>\r\n            </div>\r\n            \r\n            <div class=\"info-row\">\r\n                <div class=\"info-icon\">\r\n                    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                        <path d=\"M3,6H21V18H3V6M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M7,8A2,2 0 0,1 5,6V4A2,2 0 0,1 7,2H17A2,2 0 0,1 19,4V6A2,2 0 0,1 17,8H7Z\"/>\r\n                    </svg>\r\n                </div>\r\n                <div class=\"info-content\">\r\n                    <span class=\"info-label\">当前余额</span>\r\n                    <span class=\"balance-value\">¥{balance}</span>\r\n                </div>\r\n            </div>\r\n        </div>\r\n        <div class=\"footer\">\r\n            <p>\r\n                <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"#9ca3af\">\r\n                    <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,7V13H13V7H11M11,15V17H13V15H11Z\"/>\r\n                </svg>\r\n                此邮件由系统自动发送，请勿回复\r\n            </p>\r\n        </div>\r\n    </div>\r\n</body>\r\n</html>', '{site_name}, {username}, {amount}, {order_no}, {balance}', '2026-04-14 07:35:13', NULL),
(2, 'card_recharge', '卡密充值成功通知', '【{site_name}】卡密充值成功通知', '<!DOCTYPE html>\r\n<html>\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <style>\r\n        * {\r\n            margin: 0;\r\n            padding: 0;\r\n            box-sizing: border-box;\r\n        }\r\n        body {\r\n            font-family: -apple-system, BlinkMacSystemFont, ''Segoe UI'', Roboto, ''Helvetica Neue'', Arial, sans-serif;\r\n            background: linear-gradient(135deg, #fff7ed 0%, #ffedd5 100%);\r\n            padding: 40px 20px;\r\n            min-height: 100vh;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n        }\r\n        .card {\r\n            max-width: 480px;\r\n            margin: 0 auto;\r\n            background: #ffffff;\r\n            border-radius: 24px;\r\n            overflow: hidden;\r\n            box-shadow: 0 20px 35px -8px rgba(245, 158, 11, 0.2);\r\n        }\r\n        .header {\r\n            background: linear-gradient(135deg, #f59e0b 0%, #ef4444 100%);\r\n            padding: 36px 30px;\r\n            text-align: center;\r\n            position: relative;\r\n            overflow: hidden;\r\n        }\r\n        .header::before {\r\n            content: '''';\r\n            position: absolute;\r\n            top: -30%;\r\n            right: -15%;\r\n            width: 180px;\r\n            height: 180px;\r\n            background: rgba(255, 255, 255, 0.08);\r\n            border-radius: 50%;\r\n        }\r\n        .header::after {\r\n            content: '''';\r\n            position: absolute;\r\n            bottom: -20%;\r\n            left: -10%;\r\n            width: 140px;\r\n            height: 140px;\r\n            background: rgba(255, 255, 255, 0.05);\r\n            border-radius: 50%;\r\n        }\r\n        .header-icon {\r\n            width: 64px;\r\n            height: 64px;\r\n            background: rgba(255, 255, 255, 0.2);\r\n            border-radius: 50%;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin: 0 auto 16px;\r\n            position: relative;\r\n            z-index: 1;\r\n        }\r\n        .header-icon svg {\r\n            width: 36px;\r\n            height: 36px;\r\n            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\r\n        }\r\n        .header h2 {\r\n            color: white;\r\n            margin: 0;\r\n            font-size: 26px;\r\n            font-weight: 700;\r\n            letter-spacing: 0.5px;\r\n            position: relative;\r\n            z-index: 1;\r\n            text-shadow: 0 2px 4px rgba(0,0,0,0.1);\r\n        }\r\n        .content {\r\n            padding: 36px 30px;\r\n        }\r\n        .greeting {\r\n            font-size: 16px;\r\n            color: #1f2937;\r\n            margin-bottom: 24px;\r\n            line-height: 1.6;\r\n        }\r\n        .greeting strong {\r\n            color: #f59e0b;\r\n            font-weight: 600;\r\n        }\r\n        .amount-box {\r\n            background: linear-gradient(135deg, #fff7ed 0%, #ffedd5 100%);\r\n            border-radius: 20px;\r\n            padding: 28px 20px;\r\n            text-align: center;\r\n            margin-bottom: 28px;\r\n            border: 1px solid rgba(245, 158, 11, 0.2);\r\n        }\r\n        .amount-label {\r\n            font-size: 14px;\r\n            color: #92400e;\r\n            margin-bottom: 10px;\r\n            text-transform: uppercase;\r\n            letter-spacing: 1px;\r\n            font-weight: 500;\r\n        }\r\n        .amount-value {\r\n            font-size: 48px;\r\n            font-weight: 800;\r\n            color: #f59e0b;\r\n            line-height: 1.2;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 4px;\r\n        }\r\n        .amount-value .currency {\r\n            font-size: 28px;\r\n            font-weight: 600;\r\n        }\r\n        .info-row {\r\n            display: flex;\r\n            align-items: center;\r\n            padding: 14px 0;\r\n            border-bottom: 1px solid #f3f4f6;\r\n        }\r\n        .info-row:last-child {\r\n            border-bottom: none;\r\n        }\r\n        .info-icon {\r\n            width: 36px;\r\n            height: 36px;\r\n            background: #fef3c7;\r\n            border-radius: 10px;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin-right: 14px;\r\n        }\r\n        .info-icon svg {\r\n            width: 18px;\r\n            height: 18px;\r\n            color: #f59e0b;\r\n        }\r\n        .info-content {\r\n            flex: 1;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: space-between;\r\n        }\r\n        .info-label {\r\n            font-size: 14px;\r\n            color: #6b7280;\r\n        }\r\n        .info-value {\r\n            font-size: 15px;\r\n            font-weight: 500;\r\n            color: #1f2937;\r\n        }\r\n        .card-code {\r\n            font-family: ''Monaco'', ''Menlo'', ''Courier New'', monospace;\r\n            background: #f3f4f6;\r\n            padding: 4px 10px;\r\n            border-radius: 6px;\r\n            font-size: 14px;\r\n            letter-spacing: 0.5px;\r\n            color: #f59e0b;\r\n        }\r\n        .balance-value {\r\n            font-size: 20px;\r\n            font-weight: 700;\r\n            color: #f59e0b;\r\n        }\r\n        .footer {\r\n            text-align: center;\r\n            color: #9ca3af;\r\n            font-size: 12px;\r\n            padding: 20px 30px 30px;\r\n            border-top: 1px solid #f3f4f6;\r\n            background: #fafafa;\r\n        }\r\n        .footer p {\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 6px;\r\n        }\r\n        @media (max-width: 480px) {\r\n            body {\r\n                padding: 20px 12px;\r\n            }\r\n            .header {\r\n                padding: 28px 20px;\r\n            }\r\n            .content {\r\n                padding: 28px 20px;\r\n            }\r\n            .amount-value {\r\n                font-size: 40px;\r\n            }\r\n        }\r\n    </style>\r\n</head>\r\n<body>\r\n    <div class=\"card\">\r\n        <div class=\"header\">\r\n            <div class=\"header-icon\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"white\">\r\n                    <path d=\"M20,12H4V6H20M20,18H4V12H20M20,4H4C2.9,4 2,4.9 2,6V18C2,19.1 2.9,20 4,20H20C21.1,20 22,19.1 22,18V6C22,4.9 21.1,4 20,4Z\"/>\r\n                    <path d=\"M7,8H10V10H7V8M7,14H10V16H7V14M14,8H17V10H14V8M14,14H17V16H14V14Z\"/>\r\n                </svg>\r\n            </div>\r\n            <h2>卡密充值成功</h2>\r\n        </div>\r\n        <div class=\"content\">\r\n            <div class=\"greeting\">\r\n                尊敬的 <strong>{username}</strong>，您好！\r\n            </div>\r\n            \r\n            <div class=\"amount-box\">\r\n                <div class=\"amount-label\">充值金额</div>\r\n                <div class=\"amount-value\">\r\n                    <span class=\"currency\">¥</span>{amount}\r\n                </div>\r\n            </div>\r\n            \r\n            <div class=\"info-row\">\r\n                <div class=\"info-icon\">\r\n                    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                        <path d=\"M20,8H4V6H20V8M20,18H4V12H20V18M20,4H4C2.9,4 2,4.9 2,6V18C2,19.1 2.9,20 4,20H20C21.1,20 22,19.1 22,18V6C22,4.9 21.1,4 20,4Z\"/>\r\n                        <circle cx=\"12\" cy=\"15\" r=\"1\"/>\r\n                        <circle cx=\"12\" cy=\"9\" r=\"1\"/>\r\n                        <circle cx=\"17\" cy=\"15\" r=\"1\"/>\r\n                        <circle cx=\"17\" cy=\"9\" r=\"1\"/>\r\n                    </svg>\r\n                </div>\r\n                <div class=\"info-content\">\r\n                    <span class=\"info-label\">使用卡密</span>\r\n                    <span class=\"card-code\">{card_code}</span>\r\n                </div>\r\n            </div>\r\n            \r\n            <div class=\"info-row\">\r\n                <div class=\"info-icon\">\r\n                    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                        <path d=\"M3,6H21V18H3V6M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M7,8A2,2 0 0,1 5,6V4A2,2 0 0,1 7,2H17A2,2 0 0,1 19,4V6A2,2 0 0,1 17,8H7Z\"/>\r\n                    </svg>\r\n                </div>\r\n                <div class=\"info-content\">\r\n                    <span class=\"info-label\">当前余额</span>\r\n                    <span class=\"balance-value\">¥{balance}</span>\r\n                </div>\r\n            </div>\r\n        </div>\r\n        <div class=\"footer\">\r\n            <p>\r\n                <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"#9ca3af\">\r\n                    <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,7V13H13V7H11M11,15V17H13V15H11Z\"/>\r\n                </svg>\r\n                此邮件由系统自动发送，请勿回复\r\n            </p>\r\n        </div>\r\n    </div>\r\n</body>\r\n</html>', '{site_name}, {username}, {amount}, {card_code}, {balance}', '2026-04-14 07:37:38', NULL),
(3, 'login_alert', '登录提醒', '【{site_name}】登录提醒', '<!DOCTYPE html>\r\n<html>\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <style>\r\n        * {\r\n            margin: 0;\r\n            padding: 0;\r\n            box-sizing: border-box;\r\n        }\r\n        body {\r\n            font-family: -apple-system, BlinkMacSystemFont, ''Segoe UI'', Roboto, ''Helvetica Neue'', Arial, sans-serif;\r\n            background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%);\r\n            padding: 40px 20px;\r\n            min-height: 100vh;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n        }\r\n        .card {\r\n            max-width: 500px;\r\n            margin: 0 auto;\r\n            background: #ffffff;\r\n            border-radius: 24px;\r\n            overflow: hidden;\r\n            box-shadow: 0 20px 35px -8px rgba(102, 126, 234, 0.25);\r\n        }\r\n        .header {\r\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n            padding: 36px 30px;\r\n            text-align: center;\r\n            position: relative;\r\n            overflow: hidden;\r\n        }\r\n        .header::before {\r\n            content: '''';\r\n            position: absolute;\r\n            top: -40%;\r\n            right: -20%;\r\n            width: 200px;\r\n            height: 200px;\r\n            background: rgba(255, 255, 255, 0.08);\r\n            border-radius: 50%;\r\n        }\r\n        .header::after {\r\n            content: '''';\r\n            position: absolute;\r\n            bottom: -30%;\r\n            left: -15%;\r\n            width: 160px;\r\n            height: 160px;\r\n            background: rgba(255, 255, 255, 0.05);\r\n            border-radius: 50%;\r\n        }\r\n        .header-icon {\r\n            width: 64px;\r\n            height: 64px;\r\n            background: rgba(255, 255, 255, 0.2);\r\n            border-radius: 50%;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin: 0 auto 16px;\r\n            position: relative;\r\n            z-index: 1;\r\n        }\r\n        .header-icon svg {\r\n            width: 36px;\r\n            height: 36px;\r\n            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\r\n        }\r\n        .header h2 {\r\n            color: white;\r\n            margin: 0;\r\n            font-size: 26px;\r\n            font-weight: 700;\r\n            letter-spacing: 0.5px;\r\n            position: relative;\r\n            z-index: 1;\r\n            text-shadow: 0 2px 4px rgba(0,0,0,0.1);\r\n        }\r\n        .content {\r\n            padding: 36px 30px;\r\n        }\r\n        .greeting {\r\n            font-size: 16px;\r\n            color: #1f2937;\r\n            margin-bottom: 20px;\r\n            line-height: 1.6;\r\n        }\r\n        .greeting strong {\r\n            color: #667eea;\r\n            font-weight: 600;\r\n        }\r\n        .info-box {\r\n            background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\r\n            border-radius: 16px;\r\n            padding: 20px;\r\n            margin: 24px 0;\r\n            border: 1px solid #e2e8f0;\r\n        }\r\n        .info-row {\r\n            display: flex;\r\n            align-items: center;\r\n            padding: 10px 0;\r\n        }\r\n        .info-icon {\r\n            width: 32px;\r\n            height: 32px;\r\n            background: white;\r\n            border-radius: 8px;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin-right: 14px;\r\n            box-shadow: 0 1px 3px rgba(0,0,0,0.05);\r\n        }\r\n        .info-icon svg {\r\n            width: 18px;\r\n            height: 18;\r\n        }\r\n        .info-content {\r\n            flex: 1;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: space-between;\r\n        }\r\n        .info-label {\r\n            font-size: 14px;\r\n            color: #64748b;\r\n        }\r\n        .info-value {\r\n            font-size: 14px;\r\n            font-weight: 500;\r\n            color: #1e293b;\r\n        }\r\n        .safe-message {\r\n            margin-top: 20px;\r\n            padding: 14px 18px;\r\n            background: #f0fdf4;\r\n            border-radius: 12px;\r\n            border-left: 4px solid #10b981;\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 10px;\r\n        }\r\n        .safe-message svg {\r\n            width: 20px;\r\n            height: 20px;\r\n            color: #10b981;\r\n            flex-shrink: 0;\r\n        }\r\n        .safe-message span {\r\n            font-size: 14px;\r\n            color: #065f46;\r\n        }\r\n        .warning-message {\r\n            margin-top: 16px;\r\n            padding: 16px 18px;\r\n            background: #fef2f2;\r\n            border-radius: 12px;\r\n            border-left: 4px solid #ef4444;\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 10px;\r\n        }\r\n        .warning-message svg {\r\n            width: 20px;\r\n            height: 20px;\r\n            color: #ef4444;\r\n            flex-shrink: 0;\r\n        }\r\n        .warning-message span {\r\n            font-size: 14px;\r\n            color: #991b1b;\r\n            font-weight: 500;\r\n        }\r\n        .footer {\r\n            text-align: center;\r\n            color: #9ca3af;\r\n            font-size: 12px;\r\n            padding: 20px 30px 30px;\r\n            border-top: 1px solid #f3f4f6;\r\n            background: #fafafa;\r\n        }\r\n        .footer p {\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 6px;\r\n        }\r\n        .btn {\r\n            display: inline-block;\r\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n            color: white;\r\n            text-decoration: none;\r\n            padding: 12px 24px;\r\n            border-radius: 30px;\r\n            font-weight: 600;\r\n            font-size: 14px;\r\n            margin-top: 16px;\r\n            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);\r\n            transition: all 0.2s;\r\n        }\r\n        .btn:hover {\r\n            transform: translateY(-2px);\r\n            box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);\r\n        }\r\n        @media (max-width: 480px) {\r\n            body {\r\n                padding: 20px 12px;\r\n            }\r\n            .header {\r\n                padding: 28px 20px;\r\n            }\r\n            .content {\r\n                padding: 28px 20px;\r\n            }\r\n        }\r\n    </style>\r\n</head>\r\n<body>\r\n    <div class=\"card\">\r\n        <div class=\"header\">\r\n            <div class=\"header-icon\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"white\">\r\n                    <path d=\"M12,1L3,5V11C3,16.55 6.84,21.74 12,23C17.16,21.74 21,16.55 21,11V5L12,1M12,7C13.4,7 14.8,8.6 14.8,10V16H9.2V10C9.2,8.6 10.6,7 12,7M12,9.2C11.6,9.2 11.2,9.6 11.2,10V13.6H12.8V10C12.8,9.6 12.4,9.2 12,9.2Z\"/>\r\n                </svg>\r\n            </div>\r\n            <h2>登录提醒</h2>\r\n        </div>\r\n        <div class=\"content\">\r\n            <div class=\"greeting\">\r\n                尊敬的 <strong>{username}</strong>，您好！\r\n            </div>\r\n            <p style=\"color: #64748b; font-size: 14px;\">您的账号刚刚在以下位置登录：</p>\r\n            \r\n            <div class=\"info-box\">\r\n                <div class=\"info-row\">\r\n                    <div class=\"info-icon\">\r\n                        <svg viewBox=\"0 0 24 24\" fill=\"#667eea\">\r\n                            <path d=\"M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C6.47,22 2,17.5 2,12A10,10 0 0,1 12,2M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z\"/>\r\n                        </svg>\r\n                    </div>\r\n                    <div class=\"info-content\">\r\n                        <span class=\"info-label\">登录时间</span>\r\n                        <span class=\"info-value\">{login_time}</span>\r\n                    </div>\r\n                </div>\r\n                \r\n                <div class=\"info-row\">\r\n                    <div class=\"info-icon\">\r\n                        <svg viewBox=\"0 0 24 24\" fill=\"#667eea\">\r\n                            <path d=\"M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z\"/>\r\n                        </svg>\r\n                    </div>\r\n                    <div class=\"info-content\">\r\n                        <span class=\"info-label\">登录位置</span>\r\n                        <span class=\"info-value\">{city}</span>\r\n                    </div>\r\n                </div>\r\n                \r\n                <div class=\"info-row\">\r\n                    <div class=\"info-icon\">\r\n                        <svg viewBox=\"0 0 24 24\" fill=\"#667eea\">\r\n                            <path d=\"M22,17H18V10H22V17M22,8H18V6H22V8M4,6H2V16A2,2 0 0,0 4,18H20A2,2 0 0,0 22,16V6H4M15,17H9V10H15V17Z\"/>\r\n                        </svg>\r\n                    </div>\r\n                    <div class=\"info-content\">\r\n                        <span class=\"info-label\">设备信息</span>\r\n                        <span class=\"info-value\">{device}</span>\r\n                    </div>\r\n                </div>\r\n                \r\n                <div class=\"info-row\">\r\n                    <div class=\"info-icon\">\r\n                        <svg viewBox=\"0 0 24 24\" fill=\"#667eea\">\r\n                            <path d=\"M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4M11,7V17H13V7H11Z\"/>\r\n                        </svg>\r\n                    </div>\r\n                    <div class=\"info-content\">\r\n                        <span class=\"info-label\">IP 地址</span>\r\n                        <span class=\"info-value\">{ip}</span>\r\n                    </div>\r\n                </div>\r\n            </div>\r\n            \r\n            <div class=\"safe-message\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                    <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.6,8.1L11,13.7L7.4,10L6,11.4L11,16.5Z\"/>\r\n                </svg>\r\n                <span>如果这是您本人操作，请忽略此邮件。</span>\r\n            </div>\r\n            \r\n            <div class=\"warning-message\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                    <path d=\"M12,2L1,21H23L12,2M12,6L19.53,19H4.47L12,6M11,10V14H13V10H11M11,16V18H13V16H11Z\"/>\r\n                </svg>\r\n                <span>如果不是您本人操作，请立即修改密码！</span>\r\n            </div>\r\n            \r\n            <div style=\"text-align: center;\">\r\n                <a href=\"#\" class=\"btn\">前往修改密码</a>\r\n            </div>\r\n        </div>\r\n        <div class=\"footer\">\r\n            <p>\r\n                <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"#9ca3af\">\r\n                    <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,7V13H13V7H11M11,15V17H13V15H11Z\"/>\r\n                </svg>\r\n                此邮件由系统自动发送，请勿回复\r\n            </p>\r\n        </div>\r\n    </div>\r\n</body>\r\n</html>', '{site_name}, {username}, {login_time}, {city}, {device}, {ip}', '2026-04-14 07:40:37', NULL),
(4, 'consume', '消费成功通知', '【{site_name}】购买成功通知', '<!DOCTYPE html>\r\n<html>\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <style>\r\n        * {\r\n            margin: 0;\r\n            padding: 0;\r\n            box-sizing: border-box;\r\n        }\r\n        body {\r\n            font-family: -apple-system, BlinkMacSystemFont, ''Segoe UI'', Roboto, ''Helvetica Neue'', Arial, sans-serif;\r\n            background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%);\r\n            padding: 40px 20px;\r\n            min-height: 100vh;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n        }\r\n        .card {\r\n            max-width: 500px;\r\n            margin: 0 auto;\r\n            background: #ffffff;\r\n            border-radius: 24px;\r\n            overflow: hidden;\r\n            box-shadow: 0 20px 35px -8px rgba(102, 126, 234, 0.25);\r\n        }\r\n        .header {\r\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n            padding: 36px 30px;\r\n            text-align: center;\r\n            position: relative;\r\n            overflow: hidden;\r\n        }\r\n        .header::before {\r\n            content: '''';\r\n            position: absolute;\r\n            top: -40%;\r\n            right: -20%;\r\n            width: 200px;\r\n            height: 200px;\r\n            background: rgba(255, 255, 255, 0.08);\r\n            border-radius: 50%;\r\n        }\r\n        .header::after {\r\n            content: '''';\r\n            position: absolute;\r\n            bottom: -30%;\r\n            left: -15%;\r\n            width: 160px;\r\n            height: 160px;\r\n            background: rgba(255, 255, 255, 0.05);\r\n            border-radius: 50%;\r\n        }\r\n        .header-icon {\r\n            width: 64px;\r\n            height: 64px;\r\n            background: rgba(255, 255, 255, 0.2);\r\n            border-radius: 50%;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin: 0 auto 16px;\r\n            position: relative;\r\n            z-index: 1;\r\n        }\r\n        .header-icon svg {\r\n            width: 36px;\r\n            height: 36px;\r\n            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\r\n        }\r\n        .header h2 {\r\n            color: white;\r\n            margin: 0;\r\n            font-size: 26px;\r\n            font-weight: 700;\r\n            letter-spacing: 0.5px;\r\n            position: relative;\r\n            z-index: 1;\r\n            text-shadow: 0 2px 4px rgba(0,0,0,0.1);\r\n        }\r\n        .content {\r\n            padding: 36px 30px;\r\n        }\r\n        .greeting {\r\n            font-size: 16px;\r\n            color: #1f2937;\r\n            margin-bottom: 20px;\r\n            line-height: 1.6;\r\n        }\r\n        .greeting strong {\r\n            color: #667eea;\r\n            font-weight: 600;\r\n        }\r\n        .purchase-box {\r\n            background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\r\n            border-radius: 20px;\r\n            padding: 24px 20px;\r\n            margin: 24px 0;\r\n            border: 1px solid #e2e8f0;\r\n        }\r\n        .product-icon {\r\n            width: 56px;\r\n            height: 56px;\r\n            background: white;\r\n            border-radius: 14px;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin: 0 auto 16px;\r\n            box-shadow: 0 2px 6px rgba(0,0,0,0.05);\r\n        }\r\n        .product-icon svg {\r\n            width: 32px;\r\n            height: 32px;\r\n            color: #667eea;\r\n        }\r\n        .product-name {\r\n            text-align: center;\r\n            margin-bottom: 20px;\r\n        }\r\n        .product-name strong {\r\n            font-size: 18px;\r\n            color: #1e293b;\r\n        }\r\n        .info-row {\r\n            display: flex;\r\n            align-items: center;\r\n            padding: 12px 0;\r\n        }\r\n        .info-icon {\r\n            width: 32px;\r\n            height: 32px;\r\n            background: white;\r\n            border-radius: 8px;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin-right: 14px;\r\n            box-shadow: 0 1px 3px rgba(0,0,0,0.05);\r\n        }\r\n        .info-icon svg {\r\n            width: 18px;\r\n            height: 18px;\r\n        }\r\n        .info-content {\r\n            flex: 1;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: space-between;\r\n        }\r\n        .info-label {\r\n            font-size: 14px;\r\n            color: #64748b;\r\n        }\r\n        .info-value {\r\n            font-size: 15px;\r\n            font-weight: 500;\r\n            color: #1e293b;\r\n        }\r\n        .amount-value {\r\n            font-size: 20px;\r\n            font-weight: 700;\r\n            color: #667eea;\r\n        }\r\n        .divider {\r\n            margin: 20px 0;\r\n            border: none;\r\n            border-top: 1px solid #e5e7eb;\r\n        }\r\n        .footer {\r\n            text-align: center;\r\n            color: #9ca3af;\r\n            font-size: 12px;\r\n            padding: 20px 30px 30px;\r\n            border-top: 1px solid #f3f4f6;\r\n            background: #fafafa;\r\n        }\r\n        .footer p {\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 6px;\r\n        }\r\n        .btn {\r\n            display: inline-block;\r\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n            color: white;\r\n            text-decoration: none;\r\n            padding: 14px 32px;\r\n            border-radius: 40px;\r\n            font-weight: 600;\r\n            font-size: 15px;\r\n            margin-top: 20px;\r\n            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);\r\n            transition: all 0.2s;\r\n        }\r\n        .btn:hover {\r\n            transform: translateY(-2px);\r\n            box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);\r\n        }\r\n        @media (max-width: 480px) {\r\n            body {\r\n                padding: 20px 12px;\r\n            }\r\n            .header {\r\n                padding: 28px 20px;\r\n            }\r\n            .content {\r\n                padding: 28px 20px;\r\n            }\r\n        }\r\n    </style>\r\n</head>\r\n<body>\r\n    <div class=\"card\">\r\n        <div class=\"header\">\r\n            <div class=\"header-icon\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"white\">\r\n                    <path d=\"M17,18C15.9,18 15,18.9 15,20C15,21.1 15.9,22 17,22C18.1,22 19,21.1 19,20C19,18.9 18.1,18 17,18M1,2H4.27L5.21,4H20C20.55,4 21,4.45 21,5C21,5.17 20.95,5.34 20.88,5.5L17.3,11.97C16.96,12.58 16.3,13 15.55,13H8.1L7.2,14.63L7.17,14.75C7.17,14.89 7.28,15 7.42,15H19V17H7C5.9,17 5,16.1 5,15C5,14.65 5.09,14.32 5.24,14.04L6.6,11.59L3,4H1V2M7,18C5.9,18 5,18.9 5,20C5,21.1 5.9,22 7,22C8.1,22 9,21.1 9,20C9,18.9 8.1,18 7,18Z\"/>\r\n                </svg>\r\n            </div>\r\n            <h2>消费成功通知</h2>\r\n        </div>\r\n        <div class=\"content\">\r\n            <div class=\"greeting\">\r\n                尊敬的 <strong>{username}</strong>，您好！\r\n            </div>\r\n            \r\n            <div class=\"purchase-box\">\r\n                <div class=\"product-icon\">\r\n                    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                        <path d=\"M21,2H3C2,2 1,3 1,4V17C1,18 2,19 3,19H10.5C10.8,19.6 11.4,20 12,20C12.6,20 13.2,19.6 13.5,19H21C22,19 23,18 23,17V4C23,3 22,2 21,2M21,17H14V14H17V12H14V10H17V8H14V6H21V17M5,6H12V8H5V6M5,10H12V12H5V10M5,14H9V16H5V14Z\"/>\r\n                    </svg>\r\n                </div>\r\n                <div class=\"product-name\">\r\n                    <strong>{item_name}</strong>\r\n                </div>\r\n                \r\n                <div class=\"info-row\">\r\n                    <div class=\"info-icon\">\r\n                        <svg viewBox=\"0 0 24 24\" fill=\"#667eea\">\r\n                            <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M11,7H13V9H11V7M11,11H13V17H11V11Z\"/>\r\n                        </svg>\r\n                    </div>\r\n                    <div class=\"info-content\">\r\n                        <span class=\"info-label\">消费金额</span>\r\n                        <span class=\"amount-value\">¥{amount}</span>\r\n                    </div>\r\n                </div>\r\n                \r\n                <div class=\"info-row\">\r\n                    <div class=\"info-icon\">\r\n                        <svg viewBox=\"0 0 24 24\" fill=\"#667eea\">\r\n                            <path d=\"M20,8H4V6H20V8M20,18H4V12H20V18M20,4H4C2.9,4 2,4.9 2,6V18C2,19.1 2.9,20 4,20H20C21.1,20 22,19.1 22,18V6C22,4.9 21.1,4 20,4Z\"/>\r\n                        </svg>\r\n                    </div>\r\n                    <div class=\"info-content\">\r\n                        <span class=\"info-label\">订单号</span>\r\n                        <span class=\"info-value\">{order_no}</span>\r\n                    </div>\r\n                </div>\r\n            </div>\r\n            \r\n            <div style=\"text-align: center;\">\r\n                <a href=\"#\" class=\"btn\">查看订单详情</a>\r\n            </div>\r\n        </div>\r\n        <div class=\"footer\">\r\n            <p>\r\n                <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"#9ca3af\">\r\n                    <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,7V13H13V7H11M11,15V17H13V15H11Z\"/>\r\n                </svg>\r\n                此邮件由系统自动发送，请勿回复\r\n            </p>\r\n        </div>\r\n    </div>\r\n</body>\r\n</html>', '{site_name}, {username}, {item_name}, {amount}, {order_no}', '2026-04-14 07:43:49', NULL),
(5, 'verify_code', '验证码邮件', '【{site_name}】{type_name}验证码', '<!DOCTYPE html>\r\n<html>\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <style>\r\n        * {\r\n            margin: 0;\r\n            padding: 0;\r\n            box-sizing: border-box;\r\n        }\r\n        body {\r\n            font-family: -apple-system, BlinkMacSystemFont, ''Segoe UI'', Roboto, ''Helvetica Neue'', Arial, sans-serif;\r\n            background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%);\r\n            padding: 40px 20px;\r\n            min-height: 100vh;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n        }\r\n        .card {\r\n            max-width: 480px;\r\n            margin: 0 auto;\r\n            background: #ffffff;\r\n            border-radius: 24px;\r\n            overflow: hidden;\r\n            box-shadow: 0 20px 35px -8px rgba(102, 126, 234, 0.25);\r\n        }\r\n        .header {\r\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n            padding: 36px 30px;\r\n            text-align: center;\r\n            position: relative;\r\n            overflow: hidden;\r\n        }\r\n        .header::before {\r\n            content: '''';\r\n            position: absolute;\r\n            top: -40%;\r\n            right: -20%;\r\n            width: 200px;\r\n            height: 200px;\r\n            background: rgba(255, 255, 255, 0.08);\r\n            border-radius: 50%;\r\n        }\r\n        .header::after {\r\n            content: '''';\r\n            position: absolute;\r\n            bottom: -30%;\r\n            left: -15%;\r\n            width: 160px;\r\n            height: 160px;\r\n            background: rgba(255, 255, 255, 0.05);\r\n            border-radius: 50%;\r\n        }\r\n        .header-icon {\r\n            width: 64px;\r\n            height: 64px;\r\n            background: rgba(255, 255, 255, 0.2);\r\n            border-radius: 50%;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin: 0 auto 16px;\r\n            position: relative;\r\n            z-index: 1;\r\n        }\r\n        .header-icon svg {\r\n            width: 36px;\r\n            height: 36px;\r\n            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\r\n        }\r\n        .header h2 {\r\n            color: white;\r\n            margin: 0;\r\n            font-size: 26px;\r\n            font-weight: 700;\r\n            letter-spacing: 0.5px;\r\n            position: relative;\r\n            z-index: 1;\r\n            text-shadow: 0 2px 4px rgba(0,0,0,0.1);\r\n        }\r\n        .content {\r\n            padding: 36px 30px;\r\n        }\r\n        .greeting {\r\n            font-size: 16px;\r\n            color: #1f2937;\r\n            margin-bottom: 20px;\r\n            line-height: 1.6;\r\n        }\r\n        .operation-tip {\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 8px;\r\n            padding: 12px 0;\r\n        }\r\n        .operation-name {\r\n            color: #667eea;\r\n            font-weight: 700;\r\n            background: #e0e7ff;\r\n            padding: 2px 10px;\r\n            border-radius: 20px;\r\n            font-size: 14px;\r\n        }\r\n        .code-box {\r\n            text-align: center;\r\n            margin: 32px 0;\r\n        }\r\n        .code-label {\r\n            font-size: 13px;\r\n            color: #64748b;\r\n            margin-bottom: 12px;\r\n            text-transform: uppercase;\r\n            letter-spacing: 1px;\r\n        }\r\n        .code-value {\r\n            display: inline-block;\r\n            font-size: 36px;\r\n            font-weight: 700;\r\n            letter-spacing: 8px;\r\n            background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\r\n            padding: 18px 30px;\r\n            border-radius: 16px;\r\n            color: #1e293b;\r\n            font-family: ''Monaco'', ''Menlo'', ''Courier New'', monospace;\r\n            border: 1px dashed #cbd5e1;\r\n            text-shadow: 0 1px 2px rgba(0,0,0,0.05);\r\n        }\r\n        .expire-tip {\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 8px;\r\n            margin: 24px 0 16px;\r\n            padding: 14px 18px;\r\n            background: #fef3c7;\r\n            border-radius: 12px;\r\n            border-left: 4px solid #f59e0b;\r\n        }\r\n        .expire-tip svg {\r\n            width: 20px;\r\n            height: 20px;\r\n            color: #f59e0b;\r\n            flex-shrink: 0;\r\n        }\r\n        .expire-tip span {\r\n            font-size: 14px;\r\n            color: #92400e;\r\n        }\r\n        .expire-tip strong {\r\n            color: #d97706;\r\n            font-weight: 700;\r\n        }\r\n        .safe-tip {\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 8px;\r\n            margin: 16px 0;\r\n            padding: 14px 18px;\r\n            background: #f0fdf4;\r\n            border-radius: 12px;\r\n            border-left: 4px solid #10b981;\r\n        }\r\n        .safe-tip svg {\r\n            width: 20px;\r\n            height: 20px;\r\n            color: #10b981;\r\n            flex-shrink: 0;\r\n        }\r\n        .safe-tip span {\r\n            font-size: 14px;\r\n            color: #065f46;\r\n        }\r\n        .footer {\r\n            text-align: center;\r\n            color: #9ca3af;\r\n            font-size: 12px;\r\n            padding: 20px 30px 30px;\r\n            border-top: 1px solid #f3f4f6;\r\n            background: #fafafa;\r\n        }\r\n        .footer p {\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 6px;\r\n        }\r\n        @media (max-width: 480px) {\r\n            body {\r\n                padding: 20px 12px;\r\n            }\r\n            .header {\r\n                padding: 28px 20px;\r\n            }\r\n            .content {\r\n                padding: 28px 20px;\r\n            }\r\n            .code-value {\r\n                font-size: 28px;\r\n                letter-spacing: 5px;\r\n                padding: 14px 20px;\r\n            }\r\n        }\r\n    </style>\r\n</head>\r\n<body>\r\n    <div class=\"card\">\r\n        <div class=\"header\">\r\n            <div class=\"header-icon\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"white\">\r\n                    <path d=\"M12,1L3,5V11C3,16.55 6.84,21.74 12,23C17.16,21.74 21,16.55 21,11V5L12,1M12,7C13.4,7 14.8,8.6 14.8,10V16H9.2V10C9.2,8.6 10.6,7 12,7M12,9.2C11.6,9.2 11.2,9.6 11.2,10V13.6H12.8V10C12.8,9.6 12.4,9.2 12,9.2Z\"/>\r\n                </svg>\r\n            </div>\r\n            <h2>{type_name}</h2>\r\n        </div>\r\n        <div class=\"content\">\r\n            <div class=\"greeting\">\r\n                您好！\r\n            </div>\r\n            \r\n            <div class=\"operation-tip\">\r\n                <span>您正在进行</span>\r\n                <span class=\"operation-name\">{type_name}</span>\r\n                <span>操作，验证码如下：</span>\r\n            </div>\r\n            \r\n            <div class=\"code-box\">\r\n                <div class=\"code-label\">您的验证码</div>\r\n                <div class=\"code-value\">{code}</div>\r\n            </div>\r\n            \r\n            <div class=\"expire-tip\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                    <path d=\"M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C6.47,22 2,17.5 2,12A10,10 0 0,1 12,2M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z\"/>\r\n                </svg>\r\n                <span>验证码有效期为 <strong>5分钟</strong>，请尽快完成验证。</span>\r\n            </div>\r\n            \r\n            <div class=\"safe-tip\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                    <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.6,8.1L11,13.7L7.4,10L6,11.4L11,16.5Z\"/>\r\n                </svg>\r\n                <span>如果这不是您本人操作，请忽略此邮件。</span>\r\n            </div>\r\n        </div>\r\n        <div class=\"footer\">\r\n            <p>\r\n                <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"#9ca3af\">\r\n                    <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,7V13H13V7H11M11,15V17H13V15H11Z\"/>\r\n                </svg>\r\n                此邮件由系统自动发送，请勿回复\r\n            </p>\r\n        </div>\r\n    </div>\r\n</body>\r\n</html>', '{site_name}, {type_name}, {code}', '2026-04-14 07:45:39', NULL),
(6, 'oauth_bind', '快捷登录绑定通知', '【{site_name}】快捷登录绑定通知', '<!DOCTYPE html>\r\n<html>\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <style>\r\n        * {\r\n            margin: 0;\r\n            padding: 0;\r\n            box-sizing: border-box;\r\n        }\r\n        body {\r\n            font-family: -apple-system, BlinkMacSystemFont, ''Segoe UI'', Roboto, ''Helvetica Neue'', Arial, sans-serif;\r\n            background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%);\r\n            padding: 40px 20px;\r\n            min-height: 100vh;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n        }\r\n        .card {\r\n            max-width: 500px;\r\n            margin: 0 auto;\r\n            background: #ffffff;\r\n            border-radius: 24px;\r\n            overflow: hidden;\r\n            box-shadow: 0 20px 35px -8px rgba(102, 126, 234, 0.25);\r\n        }\r\n        .header {\r\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n            padding: 36px 30px;\r\n            text-align: center;\r\n            position: relative;\r\n            overflow: hidden;\r\n        }\r\n        .header::before {\r\n            content: '''';\r\n            position: absolute;\r\n            top: -40%;\r\n            right: -20%;\r\n            width: 200px;\r\n            height: 200px;\r\n            background: rgba(255, 255, 255, 0.08);\r\n            border-radius: 50%;\r\n        }\r\n        .header::after {\r\n            content: '''';\r\n            position: absolute;\r\n            bottom: -30%;\r\n            left: -15%;\r\n            width: 160px;\r\n            height: 160px;\r\n            background: rgba(255, 255, 255, 0.05);\r\n            border-radius: 50%;\r\n        }\r\n        .header-icon {\r\n            width: 64px;\r\n            height: 64px;\r\n            background: rgba(255, 255, 255, 0.2);\r\n            border-radius: 50%;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin: 0 auto 16px;\r\n            position: relative;\r\n            z-index: 1;\r\n        }\r\n        .header-icon svg {\r\n            width: 36px;\r\n            height: 36px;\r\n            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\r\n        }\r\n        .header h2 {\r\n            color: white;\r\n            margin: 0;\r\n            font-size: 24px;\r\n            font-weight: 700;\r\n            letter-spacing: 0.5px;\r\n            position: relative;\r\n            z-index: 1;\r\n            text-shadow: 0 2px 4px rgba(0,0,0,0.1);\r\n        }\r\n        .content {\r\n            padding: 36px 30px;\r\n        }\r\n        .greeting {\r\n            font-size: 16px;\r\n            color: #1f2937;\r\n            margin-bottom: 24px;\r\n            line-height: 1.6;\r\n        }\r\n        .greeting strong {\r\n            color: #667eea;\r\n            font-weight: 600;\r\n        }\r\n        .bind-box {\r\n            background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\r\n            border-radius: 20px;\r\n            padding: 24px 20px;\r\n            margin: 24px 0;\r\n            border: 1px solid #e2e8f0;\r\n        }\r\n        .oauth-icon {\r\n            width: 64px;\r\n            height: 64px;\r\n            background: white;\r\n            border-radius: 16px;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin: 0 auto 16px;\r\n            box-shadow: 0 2px 8px rgba(0,0,0,0.05);\r\n        }\r\n        .oauth-icon svg {\r\n            width: 36px;\r\n            height: 36px;\r\n        }\r\n        .oauth-name {\r\n            text-align: center;\r\n            font-size: 20px;\r\n            font-weight: 700;\r\n            color: #1e293b;\r\n            margin-bottom: 20px;\r\n        }\r\n        .info-row {\r\n            display: flex;\r\n            align-items: center;\r\n            padding: 12px 0;\r\n        }\r\n        .info-icon {\r\n            width: 32px;\r\n            height: 32px;\r\n            background: white;\r\n            border-radius: 8px;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            margin-right: 14px;\r\n            box-shadow: 0 1px 3px rgba(0,0,0,0.05);\r\n        }\r\n        .info-icon svg {\r\n            width: 18px;\r\n            height: 18px;\r\n            color: #667eea;\r\n        }\r\n        .info-content {\r\n            flex: 1;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: space-between;\r\n        }\r\n        .info-label {\r\n            font-size: 14px;\r\n            color: #64748b;\r\n        }\r\n        .info-value {\r\n            font-size: 14px;\r\n            font-weight: 500;\r\n            color: #1e293b;\r\n        }\r\n        .success-badge {\r\n            display: inline-flex;\r\n            align-items: center;\r\n            gap: 6px;\r\n            background: #d1fae5;\r\n            color: #065f46;\r\n            padding: 4px 12px;\r\n            border-radius: 30px;\r\n            font-size: 13px;\r\n            font-weight: 500;\r\n        }\r\n        .success-badge svg {\r\n            width: 14px;\r\n            height: 14px;\r\n        }\r\n        .warning-box {\r\n            margin-top: 20px;\r\n            padding: 16px 18px;\r\n            background: #fef2f2;\r\n            border-radius: 12px;\r\n            border-left: 4px solid #ef4444;\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 10px;\r\n        }\r\n        .warning-box svg {\r\n            width: 20px;\r\n            height: 20px;\r\n            color: #ef4444;\r\n            flex-shrink: 0;\r\n        }\r\n        .warning-box span {\r\n            font-size: 14px;\r\n            color: #991b1b;\r\n        }\r\n        .footer {\r\n            text-align: center;\r\n            color: #9ca3af;\r\n            font-size: 12px;\r\n            padding: 20px 30px 30px;\r\n            border-top: 1px solid #f3f4f6;\r\n            background: #fafafa;\r\n        }\r\n        .footer p {\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 6px;\r\n        }\r\n        .btn {\r\n            display: inline-block;\r\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n            color: white;\r\n            text-decoration: none;\r\n            padding: 12px 24px;\r\n            border-radius: 30px;\r\n            font-weight: 600;\r\n            font-size: 14px;\r\n            margin-top: 16px;\r\n            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);\r\n            transition: all 0.2s;\r\n        }\r\n        .btn:hover {\r\n            transform: translateY(-2px);\r\n            box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);\r\n        }\r\n        @media (max-width: 480px) {\r\n            body {\r\n                padding: 20px 12px;\r\n            }\r\n            .header {\r\n                padding: 28px 20px;\r\n            }\r\n            .content {\r\n                padding: 28px 20px;\r\n            }\r\n            .header h2 {\r\n                font-size: 20px;\r\n            }\r\n        }\r\n    </style>\r\n</head>\r\n<body>\r\n    <div class=\"card\">\r\n        <div class=\"header\">\r\n            <div class=\"header-icon\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"white\">\r\n                    <path d=\"M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4M11,7V17H13V7H11Z\"/>\r\n                </svg>\r\n            </div>\r\n            <h2>{oauth_type} 快捷登录绑定成功</h2>\r\n        </div>\r\n        <div class=\"content\">\r\n            <div class=\"greeting\">\r\n                尊敬的 <strong>{username}</strong>，您好！\r\n            </div>\r\n            \r\n            <div class=\"bind-box\">\r\n                <div class=\"oauth-icon\">\r\n                    <svg viewBox=\"0 0 24 24\" fill=\"#667eea\">\r\n                        <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M11,7H13V9H11V7M11,11H13V17H11V11Z\"/>\r\n                    </svg>\r\n                </div>\r\n                <div class=\"oauth-name\">{oauth_type}</div>\r\n                \r\n                <div style=\"display: flex; justify-content: center; margin-bottom: 16px;\">\r\n                    <span class=\"success-badge\">\r\n                        <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                            <path d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"/>\r\n                        </svg>\r\n                        绑定成功\r\n                    </span>\r\n                </div>\r\n                \r\n                <div class=\"info-row\">\r\n                    <div class=\"info-icon\">\r\n                        <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                            <path d=\"M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C6.47,22 2,17.5 2,12A10,10 0 0,1 12,2M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z\"/>\r\n                        </svg>\r\n                    </div>\r\n                    <div class=\"info-content\">\r\n                        <span class=\"info-label\">绑定时间</span>\r\n                        <span class=\"info-value\">{bind_time}</span>\r\n                    </div>\r\n                </div>\r\n            </div>\r\n            \r\n            <div class=\"warning-box\">\r\n                <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n                    <path d=\"M12,2L1,21H23L12,2M12,6L19.53,19H4.47L12,6M11,10V14H13V10H11M11,16V18H13V16H11Z\"/>\r\n                </svg>\r\n                <span>如非本人操作，请立即修改密码并联系客服。</span>\r\n            </div>\r\n            \r\n            <div style=\"text-align: center;\">\r\n                <a href=\"#\" class=\"btn\">修改密码</a>\r\n            </div>\r\n        </div>\r\n        <div class=\"footer\">\r\n            <p>\r\n                <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"#9ca3af\">\r\n                    <path d=\"M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,7V13H13V7H11M11,15V17H13V15H11Z\"/>\r\n                </svg>\r\n                此邮件由系统自动发送，请勿回复\r\n            </p>\r\n        </div>\r\n    </div>\r\n</body>\r\n</html>', '{site_name}, {username}, {oauth_type}, {bind_time}', '2026-04-14 07:47:17', NULL);

-- ----------------------------
-- 10. 邮箱验证码表
-- ----------------------------
DROP TABLE IF EXISTS `email_verify_codes`;
CREATE TABLE `email_verify_codes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(100) NOT NULL,
  `code` varchar(10) NOT NULL,
  `type` enum('register','login','bind','reset') DEFAULT 'register',
  `used` tinyint(4) DEFAULT '0',
  `expired_at` datetime NOT NULL,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_email` (`email`),
  KEY `idx_code` (`code`),
  KEY `idx_expired` (`expired_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='邮箱验证码表';

-- ----------------------------
-- 11. 优惠券表
-- ----------------------------
DROP TABLE IF EXISTS `encrypt_coupons`;
CREATE TABLE `encrypt_coupons` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(50) NOT NULL,
  `name` varchar(100) DEFAULT NULL,
  `type` enum('fixed','percent') DEFAULT 'fixed',
  `value` decimal(10,2) NOT NULL,
  `min_amount` decimal(10,2) DEFAULT '0.00',
  `max_discount` decimal(10,2) DEFAULT NULL,
  `usage_limit` int(11) DEFAULT '1',
  `used_count` int(11) DEFAULT '0',
  `expire_date` datetime DEFAULT NULL,
  `applicable_packages` text,
  `status` tinyint(4) DEFAULT '1',
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 12. 优惠券使用记录表
-- ----------------------------
DROP TABLE IF EXISTS `encrypt_coupon_usage`;
CREATE TABLE `encrypt_coupon_usage` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `coupon_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `order_amount` decimal(10,2) NOT NULL,
  `discount_amount` decimal(10,2) NOT NULL,
  `used_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_user` (`user_id`),
  KEY `idx_coupon` (`coupon_id`),
  CONSTRAINT `encrypt_coupon_usage_ibfk_1` FOREIGN KEY (`coupon_id`) REFERENCES `encrypt_coupons` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 13. 加密历史记录表
-- ----------------------------
DROP TABLE IF EXISTS `encrypt_history`;
CREATE TABLE `encrypt_history` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `original_name` varchar(255) NOT NULL,
  `file_size` int(11) DEFAULT NULL,
  `encrypted_size` int(11) DEFAULT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `bind_domain` varchar(255) DEFAULT NULL,
  `bind_type` enum('exact','wildcard') DEFAULT 'exact',
  `language` varchar(20) DEFAULT 'php',
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='加密历史记录';

-- ----------------------------
-- 14. 加密套餐表
-- ----------------------------
DROP TABLE IF EXISTS `encrypt_packages`;
CREATE TABLE `encrypt_packages` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `days` int(11) DEFAULT NULL,
  `price` decimal(10,2) NOT NULL,
  `sort_order` int(11) DEFAULT '0',
  `status` tinyint(4) DEFAULT '1',
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 15. 加密服务购买记录表
-- ----------------------------
DROP TABLE IF EXISTS `encrypt_services`;
CREATE TABLE `encrypt_services` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `service_name` varchar(100) NOT NULL DEFAULT 'php_encrypt',
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `expire_date` datetime DEFAULT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `package_type` varchar(20) DEFAULT 'permanent',
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `idx_expire_date` (`expire_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='加密服务购买记录';

-- ----------------------------
-- 16. 邀请记录表
-- ----------------------------
DROP TABLE IF EXISTS `invite_records`;
CREATE TABLE `invite_records` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `inviter_id` int(11) NOT NULL,
  `invitee_id` int(11) NOT NULL,
  `invite_code` varchar(20) NOT NULL,
  `status` tinyint(4) DEFAULT '0',
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_inviter` (`inviter_id`),
  KEY `idx_invitee` (`invitee_id`),
  KEY `idx_code` (`invite_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 17. 授权码/license表
-- ----------------------------
DROP TABLE IF EXISTS `licenses`;
CREATE TABLE `licenses` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `license_code` varchar(64) NOT NULL,
  `app_id` int(11) NOT NULL,
  `app_key` varchar(64) NOT NULL DEFAULT '',
  `user_id` int(11) NOT NULL,
  `domain` varchar(255) DEFAULT NULL,
  `expire_date` date DEFAULT NULL,
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `used` tinyint(1) NOT NULL DEFAULT '0',
  `used_at` datetime DEFAULT NULL,
  `used_domain` varchar(255) DEFAULT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `license_code` (`license_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 18. 导航链接表
-- ----------------------------
DROP TABLE IF EXISTS `links`;
CREATE TABLE `links` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `url` varchar(255) NOT NULL,
  `type` enum('friend','header') NOT NULL DEFAULT 'friend',
  `sort` int(11) NOT NULL DEFAULT '0',
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `parent_id` int(11) DEFAULT '0' COMMENT '父菜单ID，0表示顶级菜单',
  `is_dropdown` tinyint(4) DEFAULT '0' COMMENT '是否作为下拉菜单：1是 0否',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT IGNORE INTO `links` (`id`, `name`, `url`, `type`, `sort`, `status`, `parent_id`, `is_dropdown`) VALUES
(1, '首页', '/', 'header', 1, 1, 0, 0),
(2, '授权查询', '/modules/home/query.php', 'header', 2, 1, 15, 0),
(3, '购买授权', '/modules/home/buy.php', 'header', 3, 1, 15, 0),
(4, '联系我们', '/modules/home/contact.php', 'header', 4, 1, 15, 0),
(5, '官网', 'https://www.lhwks.cn', 'friend', 0, 1, 0, 0),
(6, '关于我们', '/modules/home/about.php', 'header', 5, 1, 15, 0),
(7, 'PHP加密', '/encrypt', 'header', 7, 1, 0, 0),
(8, '技术文档', '/modules/home/faq.php', 'header', 9, 1, 0, 0),
(9, '常用功能', '#', 'header', 2, 1, 0, 1);

-- ----------------------------
-- 19. 登录失败记录表
-- ----------------------------
DROP TABLE IF EXISTS `login_fail_log`;
CREATE TABLE `login_fail_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(45) NOT NULL,
  `username` varchar(100) DEFAULT NULL,
  `fail_count` int(11) DEFAULT '1',
  `last_fail_time` datetime NOT NULL,
  `lock_until` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_ip` (`ip`),
  KEY `idx_lock_until` (`lock_until`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='登录失败记录表';

-- ----------------------------
-- 20. 扫码登录二维码记录表
-- ----------------------------
DROP TABLE IF EXISTS `login_qrcodes`;
CREATE TABLE `login_qrcodes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `token` varchar(64) NOT NULL COMMENT '唯一标识',
  `status` tinyint(1) DEFAULT '0' COMMENT '0待扫描 1已扫描待确认 2已确认 3已过期',
  `scan_user_id` int(11) DEFAULT '0' COMMENT '扫码用户ID',
  `confirm_user_id` int(11) DEFAULT '0' COMMENT '确认登录用户ID',
  `scan_ip` varchar(45) DEFAULT NULL COMMENT '扫码IP',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `expire_at` datetime DEFAULT NULL COMMENT '过期时间',
  `site_id` varchar(64) DEFAULT NULL COMMENT '分站标识',
  `site_url` varchar(255) DEFAULT NULL COMMENT '分站地址',
  `app_user_id` int(11) DEFAULT '0' COMMENT 'App端确认用户ID',
  `generate_user_id` int(11) DEFAULT '0' COMMENT '生成二维码的用户ID',
  PRIMARY KEY (`id`),
  UNIQUE KEY `token` (`token`),
  KEY `idx_status` (`status`),
  KEY `idx_expire` (`expire_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='扫码登录二维码记录';

-- ----------------------------
-- 21. 通知表
-- ----------------------------
DROP TABLE IF EXISTS `notifications`;
CREATE TABLE `notifications` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL DEFAULT '0' COMMENT '0表示所有用户',
  `type` varchar(50) DEFAULT 'system',
  `title` varchar(255) NOT NULL,
  `content` text,
  `is_read` tinyint(1) DEFAULT '0',
  `status` tinyint(1) DEFAULT '1',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `is_read` (`is_read`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 22. 订单表
-- ----------------------------
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_no` varchar(32) NOT NULL,
  `user_id` int(11) NOT NULL,
  `type` enum('app','group','recharge') NOT NULL DEFAULT 'app',
  `item_id` int(11) DEFAULT NULL,
  `amount` decimal(10,2) NOT NULL,
  `payment_method` varchar(50) DEFAULT NULL,
  `status` enum('pending','paid','cancelled') NOT NULL DEFAULT 'pending',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `expire_at` datetime DEFAULT NULL,
  `paid_at` datetime DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `order_no` (`order_no`),
  KEY `idx_expire_at` (`expire_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 23. PC端登录请求表
-- ----------------------------
DROP TABLE IF EXISTS `pc_login_requests`;
CREATE TABLE `pc_login_requests` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `token` varchar(64) NOT NULL COMMENT '登录token',
  `user_id` int(11) DEFAULT '0' COMMENT '确认的用户ID',
  `status` tinyint(1) DEFAULT '0' COMMENT '0等待确认 1已确认 2已拒绝 3已过期',
  `device_name` varchar(100) DEFAULT NULL COMMENT '设备名称',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  `expire_at` datetime DEFAULT NULL,
  `confirmed_at` datetime DEFAULT NULL,
  `device_id` varchar(64) DEFAULT NULL COMMENT '设备标识',
  PRIMARY KEY (`id`),
  UNIQUE KEY `token` (`token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='PC端登录请求';

-- ----------------------------
-- 24. PC端记住登录状态表
-- ----------------------------
DROP TABLE IF EXISTS `pc_login_sessions`;
CREATE TABLE `pc_login_sessions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '用户ID',
  `session_token` varchar(64) NOT NULL COMMENT '会话凭证',
  `device_id` varchar(64) DEFAULT NULL COMMENT '设备标识',
  `device_name` varchar(100) DEFAULT NULL COMMENT '设备名称',
  `last_login` datetime DEFAULT CURRENT_TIMESTAMP,
  `is_active` tinyint(1) DEFAULT '1',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `session_token` (`session_token`),
  KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='PC端记住登录状态';

-- ----------------------------
-- 25. 快捷登录配置表
-- ----------------------------
DROP TABLE IF EXISTS `quick_login_config`;
CREATE TABLE `quick_login_config` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(30) NOT NULL,
  `name` varchar(50) NOT NULL,
  `enabled` tinyint(4) DEFAULT '0',
  `sort` int(11) DEFAULT '0',
  `icon` varchar(255) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `type` (`type`),
  KEY `idx_enabled` (`enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 26. 返佣记录表
-- ----------------------------
DROP TABLE IF EXISTS `rebate_records`;
CREATE TABLE `rebate_records` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `from_user_id` int(11) NOT NULL,
  `order_id` int(11) NOT NULL,
  `amount` decimal(10,2) NOT NULL,
  `rate` decimal(5,2) NOT NULL,
  `status` tinyint(4) DEFAULT '0',
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `settled_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_user` (`user_id`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 27. 扫码设备绑定表
-- ----------------------------
DROP TABLE IF EXISTS `scan_devices`;
CREATE TABLE `scan_devices` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '用户ID',
  `device_id` varchar(64) NOT NULL COMMENT '设备唯一标识',
  `device_name` varchar(100) DEFAULT NULL COMMENT '设备名称',
  `status` tinyint(1) DEFAULT '1' COMMENT '1正常 0禁用',
  `last_active` datetime DEFAULT NULL COMMENT '最后活跃时间',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_device` (`user_id`,`device_id`),
  KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='扫码设备绑定';

-- ----------------------------
-- 28. 站点配置表
-- ----------------------------
DROP TABLE IF EXISTS `site_config`;
CREATE TABLE `site_config` (
  `key` varchar(100) NOT NULL,
  `value` text,
  PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT IGNORE INTO `site_config` (`key`, `value`) VALUES
('contact_config', '{\"qq_group\":\"\",\"email\":\"\",\"service_time\":\"\\u5468\\u4e00\\u81f3\\u5468\\u4e94 9:00-18:00\",\"company_address\":\"\\u4e2d\\u56fd \\u00b7 \\u6c5f\\u82cf\",\"business_tip\":\"\\u975e\\u5de5\\u4f5c\\u65f6\\u95f4\\u63d0\\u4ea4\\u7684\\u95ee\\u9898\\uff0c\\u6211\\u4eec\\u4f1a\\u5728\\u4e0b\\u4e00\\u4e2a\\u5de5\\u4f5c\\u65e5\\u7b2c\\u4e00\\u65f6\\u95f4\\u5904\\u7406\",\"wechat_qrcode\":\"https:\\/\\/cs.demo.lhwks.cn\\/assets\\/uploads\\/wechat_qrcode.jpg\"}'),
('contact_info', ''),
('oauth_gitee_callback', ''),
('oauth_gitee_client_id', ''),
('oauth_gitee_client_secret', ''),
('oauth_github_client_id', ''),
('oauth_github_client_secret', ''),
('oauth_lhouath_app_id', ''),
('oauth_lhouath_app_secret', ''),
('oauth_union_api_url', ''),
('oauth_union_app_id', ''),
('oauth_union_app_key', ''),
('oauth_union_callback', ''),
('payment_config', '{\"api_url\":\"\",\"pid\":\"\",\"key\":\"\",\"pay_alipay\":1,\"pay_wxpay\":1}'),
('register_enabled', '1'),
('site_copyright', 'Copyright © 2023-2026 麟泓网科室 All rights reserved. 版权所有'),
('site_icp', ''),
('site_keywords', '授权,软件授权,在线授权'),
('site_logo', '/assets/uploads/logo_1775736137.png'),
('site_name', '麟泓授权系统'),
('site_sidebar_icon', '/assets/uploads/sidebar_icon_1776063943.png'),
('smtp_from', ''),
('smtp_host', 'smtp.exmail.qq.com'),
('smtp_name', 'PHP加密工具'),
('smtp_pass', ''),
('smtp_port', ''),
('smtp_user', ''),
('sys_version', '3.1.8');

-- ----------------------------
-- 29. 工单表
-- ----------------------------
DROP TABLE IF EXISTS `tickets`;
CREATE TABLE `tickets` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title` varchar(255) NOT NULL,
  `content` text NOT NULL,
  `status` enum('open','processing','closed') NOT NULL DEFAULT 'open',
  `priority` enum('low','medium','high') NOT NULL DEFAULT 'medium',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 30. 工单回复表
-- ----------------------------
DROP TABLE IF EXISTS `ticket_replies`;
CREATE TABLE `ticket_replies` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ticket_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `content` text NOT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 31. 更新包表
-- ----------------------------
DROP TABLE IF EXISTS `updates`;
CREATE TABLE `updates` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `version` varchar(20) NOT NULL,
  `release_notes` text,
  `package_url` varchar(255) NOT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `version` (`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 32. 用户表
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(255) NOT NULL,
  `email` varchar(100) DEFAULT '',
  `avatar` varchar(255) DEFAULT '/assets/uploads/default_avatar.png',
  `balance` decimal(10,2) NOT NULL DEFAULT '0.00',
  `group_id` int(11) DEFAULT NULL,
  `role` enum('admin','employee','user') NOT NULL DEFAULT 'user',
  `is_agent` tinyint(1) NOT NULL DEFAULT '0',
  `agent_commission_rate` decimal(5,2) NOT NULL DEFAULT '0.00',
  `agent_total_commission` decimal(10,2) NOT NULL DEFAULT '0.00',
  `agent_withdrawn` decimal(10,2) NOT NULL DEFAULT '0.00',
  `agent_level` tinyint(1) NOT NULL DEFAULT '1',
  `permissions` json DEFAULT NULL,
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `email_notify` tinyint(4) DEFAULT '1',
  `last_notify_at` datetime DEFAULT NULL,
  `email_verified` tinyint(4) DEFAULT '0',
  `gitee_id` varchar(100) DEFAULT NULL,
  `gitee_name` varchar(100) DEFAULT NULL,
  `avatar_url` varchar(500) DEFAULT NULL,
  `union_id` varchar(100) DEFAULT NULL,
  `union_type` varchar(20) DEFAULT NULL,
  `need_change_password` tinyint(4) DEFAULT '0',
  `is_oauth_user` tinyint(4) DEFAULT '0',
  `invite_code` varchar(20) DEFAULT NULL,
  `rebate_balance` decimal(10,2) DEFAULT '0.00',
  `feishu_id` varchar(100) DEFAULT NULL,
  `feishu_name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  UNIQUE KEY `email` (`email`),
  KEY `idx_gitee_id` (`gitee_id`),
  KEY `idx_union_id` (`union_id`),
  KEY `idx_invite_code` (`invite_code`),
  KEY `idx_feishu_id` (`feishu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 33. 用户组表
-- ----------------------------
DROP TABLE IF EXISTS `user_groups`;
CREATE TABLE `user_groups` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `description` text,
  `price` decimal(10,2) NOT NULL DEFAULT '0.00',
  `discount` decimal(5,2) NOT NULL DEFAULT '100.00',
  `duration_days` int(11) NOT NULL DEFAULT '0',
  `sort` int(11) NOT NULL DEFAULT '0',
  `status` tinyint(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 34. 用户组购买记录表
-- ----------------------------
DROP TABLE IF EXISTS `user_group_purchases`;
CREATE TABLE `user_group_purchases` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `group_id` int(11) NOT NULL,
  `expire_date` date DEFAULT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 35. 用户登录日志表
-- ----------------------------
DROP TABLE IF EXISTS `user_login_log`;
CREATE TABLE `user_login_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `ip` varchar(45) NOT NULL,
  `city` varchar(100) DEFAULT NULL,
  `device` varchar(255) DEFAULT NULL,
  `user_agent` varchar(255) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_ip` (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 36. 用户OAuth绑定表
-- ----------------------------
DROP TABLE IF EXISTS `user_oauth_bindings`;
CREATE TABLE `user_oauth_bindings` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `oauth_type` varchar(30) NOT NULL,
  `openid` varchar(100) NOT NULL,
  `nickname` varchar(100) DEFAULT NULL,
  `avatar` varchar(500) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_oauth` (`user_id`,`oauth_type`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_oauth` (`oauth_type`,`openid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- 37. 用户版本更新提醒记录表
-- ----------------------------
DROP TABLE IF EXISTS `user_version_reminders`;
CREATE TABLE `user_version_reminders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `version` varchar(20) NOT NULL,
  `reminded_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_user_version` (`user_id`,`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户版本更新提醒记录表';

-- ----------------------------
-- 38. 微信绑定验证码表
-- ----------------------------
DROP TABLE IF EXISTS `wechat_bind_tokens`;
CREATE TABLE `wechat_bind_tokens` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `token` varchar(32) NOT NULL COMMENT '绑定token',
  `openid` varchar(64) DEFAULT NULL COMMENT '用户openid',
  `code` varchar(10) NOT NULL COMMENT '6位验证码',
  `status` tinyint(1) DEFAULT '0' COMMENT '0待验证 1已验证 2已完成',
  `expire_at` datetime DEFAULT NULL COMMENT '过期时间',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `token` (`token`),
  KEY `idx_code` (`code`),
  KEY `idx_openid` (`openid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信绑定验证码表';

-- ----------------------------
-- 39. 微信登录验证码表
-- ----------------------------
DROP TABLE IF EXISTS `wechat_login_tokens`;
CREATE TABLE `wechat_login_tokens` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `token` varchar(32) NOT NULL COMMENT '登录token',
  `openid` varchar(64) DEFAULT NULL COMMENT '用户openid',
  `code` varchar(10) NOT NULL COMMENT '6位验证码',
  `status` tinyint(1) DEFAULT '0' COMMENT '0待验证 1已验证 2已登录',
  `expire_at` datetime DEFAULT NULL COMMENT '过期时间',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `token` (`token`),
  KEY `idx_code` (`code`),
  KEY `idx_openid` (`openid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信登录验证码表';

-- ============================================
-- 开启外键检查
-- ============================================
SET FOREIGN_KEY_CHECKS = 1;