Files
SmsReceiver-go/database/database.go
OpenClaw Agent 4a31cd1115 initial commit: Go version of SMS Receiver with fixed template rendering
- Implemented all core features from Python version
- Fixed int64/int type compatibility in template functions
- Added login authentication, SMS receiving, statistics, logs
- Independent database: sms_receiver_go.db
- Fixed frontend display issues for message list and statistics
2026-02-08 17:15:22 +08:00

326 lines
7.7 KiB
Go

package database
import (
"database/sql"
"fmt"
"log"
"strings"
"time"
"sms-receiver-go/config"
"sms-receiver-go/models"
_ "github.com/mattn/go-sqlite3"
)
var db *sql.DB
// Init 初始化数据库
func Init(cfg *config.DatabaseConfig) error {
var err error
db, err = sql.Open("sqlite3", cfg.Path)
if err != nil {
return fmt.Errorf("打开数据库失败: %w", err)
}
if err = db.Ping(); err != nil {
return fmt.Errorf("数据库连接失败: %w", err)
}
// 创建表
if err = createTables(); err != nil {
return fmt.Errorf("创建表失败: %w", err)
}
log.Printf("数据库初始化成功: %s", cfg.Path)
return nil
}
// createTables 创建数据表
func createTables() error {
// 短信消息表
createMessagesSQL := `
CREATE TABLE IF NOT EXISTS sms_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
from_number TEXT NOT NULL,
content TEXT NOT NULL,
timestamp INTEGER NOT NULL,
device_info TEXT,
sim_info TEXT,
sign_verified INTEGER,
ip_address TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
`
// 接收日志表
createLogsSQL := `
CREATE TABLE IF NOT EXISTS receive_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
from_number TEXT NOT NULL,
content TEXT NOT NULL,
timestamp INTEGER NOT NULL,
sign TEXT,
sign_valid INTEGER,
ip_address TEXT,
status TEXT NOT NULL,
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
`
// 创建索引
createIndexesSQL := `
CREATE INDEX IF NOT EXISTS idx_messages_from ON sms_messages(from_number);
CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON sms_messages(timestamp);
CREATE INDEX IF NOT EXISTS idx_messages_created ON sms_messages(created_at);
CREATE INDEX IF NOT EXISTS idx_logs_created ON receive_logs(created_at);
`
statements := []string{createMessagesSQL, createLogsSQL, createIndexesSQL}
for _, stmt := range statements {
if _, err := db.Exec(stmt); err != nil {
return fmt.Errorf("执行 SQL 失败: %w", err)
}
}
return nil
}
// InsertMessage 插入短信消息
func InsertMessage(msg *models.SMSMessage) (int64, error) {
result, err := db.Exec(`
INSERT INTO sms_messages (from_number, content, timestamp, device_info, sim_info, sign_verified, ip_address)
VALUES (?, ?, ?, ?, ?, ?, ?)
`,
msg.FromNumber,
msg.Content,
msg.Timestamp,
msg.DeviceInfo,
msg.SIMInfo,
msg.SignVerified,
msg.IPAddress,
)
if err != nil {
return 0, fmt.Errorf("插入消息失败: %w", err)
}
return result.LastInsertId()
}
// InsertLog 插入接收日志
func InsertLog(log *models.ReceiveLog) (int64, error) {
result, err := db.Exec(`
INSERT INTO receive_logs (from_number, content, timestamp, sign, sign_valid, ip_address, status, error_message)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`,
log.FromNumber,
log.Content,
log.Timestamp,
log.Sign,
log.SignValid,
log.IPAddress,
log.Status,
log.ErrorMessage,
)
if err != nil {
return 0, fmt.Errorf("插入日志失败: %w", err)
}
return result.LastInsertId()
}
// GetMessages 获取短信列表
func GetMessages(page, limit int, from string, search string) ([]models.SMSMessage, int64, error) {
offset := (page - 1) * limit
// 构建查询条件
var conditions []string
var args []interface{}
if from != "" {
conditions = append(conditions, "from_number = ?")
args = append(args, from)
}
if search != "" {
conditions = append(conditions, "(from_number LIKE ? OR content LIKE ?)")
args = append(args, "%"+search+"%", "%"+search+"%")
}
whereClause := ""
if len(conditions) > 0 {
whereClause = "WHERE " + strings.Join(conditions, " AND ")
}
// 查询总数
var total int64
countSQL := fmt.Sprintf("SELECT COUNT(*) FROM sms_messages %s", whereClause)
if err := db.QueryRow(countSQL, args...).Scan(&total); err != nil {
return nil, 0, fmt.Errorf("查询总数失败: %w", err)
}
// 查询数据(按短信时间戳排序,与 Python 版本一致)
querySQL := fmt.Sprintf(`
SELECT id, from_number, content, timestamp, device_info, sim_info, sign_verified, ip_address, created_at
FROM sms_messages
%s
ORDER BY timestamp DESC, id DESC
LIMIT ? OFFSET ?
`, whereClause)
args = append(args, limit, offset)
rows, err := db.Query(querySQL, args...)
if err != nil {
return nil, 0, fmt.Errorf("查询消息失败: %w", err)
}
defer rows.Close()
var messages []models.SMSMessage
for rows.Next() {
var msg models.SMSMessage
err := rows.Scan(
&msg.ID,
&msg.FromNumber,
&msg.Content,
&msg.Timestamp,
&msg.DeviceInfo,
&msg.SIMInfo,
&msg.SignVerified,
&msg.IPAddress,
&msg.CreatedAt,
)
if err != nil {
return nil, 0, fmt.Errorf("扫描消息失败: %w", err)
}
messages = append(messages, msg)
}
return messages, total, nil
}
// GetMessageByID 根据 ID 获取消息详情
func GetMessageByID(id int64) (*models.SMSMessage, error) {
var msg models.SMSMessage
err := db.QueryRow(`
SELECT id, from_number, content, timestamp, device_info, sim_info, sign_verified, ip_address, created_at
FROM sms_messages WHERE id = ?
`, id).Scan(
&msg.ID,
&msg.FromNumber,
&msg.Content,
&msg.Timestamp,
&msg.DeviceInfo,
&msg.SIMInfo,
&msg.SignVerified,
&msg.IPAddress,
&msg.CreatedAt,
)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, fmt.Errorf("查询消息失败: %w", err)
}
return &msg, nil
}
// GetStatistics 获取统计信息
func GetStatistics() (*models.Statistics, error) {
stats := &models.Statistics{}
// 总数
if err := db.QueryRow("SELECT COUNT(*) FROM sms_messages").Scan(&stats.Total); err != nil {
return nil, err
}
// 今日数量
today := time.Now().Format("2006-01-02")
if err := db.QueryRow("SELECT COUNT(*) FROM sms_messages WHERE date(created_at) = ?", today).Scan(&stats.Today); err != nil {
return nil, err
}
// 本周数量
weekStart := time.Now().AddDate(0, 0, -int(time.Now().Weekday())+1).Format("2006-01-02")
if err := db.QueryRow("SELECT COUNT(*) FROM sms_messages WHERE created_at >= ?", weekStart).Scan(&stats.Week); err != nil {
return nil, err
}
// 签名验证通过数量
if err := db.QueryRow("SELECT COUNT(*) FROM sms_messages WHERE sign_verified = 1").Scan(&stats.Verified); err != nil {
return nil, err
}
// 签名验证未通过数量
if err := db.QueryRow("SELECT COUNT(*) FROM sms_messages WHERE sign_verified = 0").Scan(&stats.Unverified); err != nil {
return nil, err
}
return stats, nil
}
// GetLogs 获取接收日志
func GetLogs(page, limit int) ([]models.ReceiveLog, int64, error) {
offset := (page - 1) * limit
// 查询总数
var total int64
if err := db.QueryRow("SELECT COUNT(*) FROM receive_logs").Scan(&total); err != nil {
return nil, 0, err
}
rows, err := db.Query(`
SELECT id, from_number, content, timestamp, sign, sign_valid, ip_address, status, error_message, created_at
FROM receive_logs
ORDER BY created_at DESC
LIMIT ? OFFSET ?
`, limit, offset)
if err != nil {
return nil, 0, err
}
defer rows.Close()
var logs []models.ReceiveLog
for rows.Next() {
var log models.ReceiveLog
err := rows.Scan(
&log.ID,
&log.FromNumber,
&log.Content,
&log.Timestamp,
&log.Sign,
&log.SignValid,
&log.IPAddress,
&log.Status,
&log.ErrorMessage,
&log.CreatedAt,
)
if err != nil {
return nil, 0, err
}
logs = append(logs, log)
}
return logs, total, nil
}
// CleanupOldMessages 清理旧消息
func CleanupOldMessages(days int) (int64, error) {
cutoff := time.Now().AddDate(0, 0, -days).Format("2006-01-02 15:04:05")
result, err := db.Exec("DELETE FROM sms_messages WHERE created_at < ?", cutoff)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
// GetDB 获取数据库实例
func GetDB() *sql.DB {
return db
}
// Close 关闭数据库连接
func Close() error {
if db != nil {
return db.Close()
}
return nil
}