Files

157 lines
3.4 KiB
Go
Raw Permalink Normal View History

package db
import (
"database/sql"
"fmt"
"regexp"
"strings"
"sync"
"time"
_ "github.com/lib/pq"
"web-giao-ban-go/config"
"web-giao-ban-go/logger"
)
var (
poolMu sync.RWMutex
pools = make(map[string]*sql.DB)
)
func GetConnectionString(cfg *config.AppConfig, dbName string) string {
if dbName == "" {
dbName = cfg.Database.DefaultDB
}
pass := cfg.DecryptedDBPassword
if pass == "" {
pass, _ = config.Decrypt(cfg.Database.Password)
}
return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable connect_timeout=10 statement_timeout=15000",
cfg.Database.Host,
cfg.Database.Port,
cfg.Database.User,
pass,
dbName,
)
}
func GetDB(dbName string) (*sql.DB, error) {
cfg := config.Get()
if dbName == "" {
dbName = cfg.Database.DefaultDB
}
poolMu.RLock()
if conn, ok := pools[dbName]; ok {
poolMu.RUnlock()
return conn, nil
}
poolMu.RUnlock()
poolMu.Lock()
defer poolMu.Unlock()
// Double check
if conn, ok := pools[dbName]; ok {
return conn, nil
}
connStr := GetConnectionString(cfg, dbName)
conn, err := sql.Open("postgres", connStr)
if err != nil {
logger.Error("Lỗi kết nối PostgreSQL [%s]: %v", dbName, err)
return nil, err
}
conn.SetMaxOpenConns(15)
conn.SetMaxIdleConns(5)
conn.SetConnMaxLifetime(15 * time.Minute)
if err := conn.Ping(); err != nil {
logger.Error("Ping thất bại tới CSDL [%s]: %v", dbName, err)
return nil, err
}
pools[dbName] = conn
logger.Info("Khởi tạo connection pool tới CSDL: %s", dbName)
return conn, nil
}
func CloseAll() {
poolMu.Lock()
defer poolMu.Unlock()
for name, conn := range pools {
_ = conn.Close()
delete(pools, name)
}
}
func TestConnection(host string, port int, user, pass, dbName string) error {
connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable connect_timeout=5",
host, port, user, pass, dbName)
testConn, err := sql.Open("postgres", connStr)
if err != nil {
return err
}
defer testConn.Close()
return testConn.Ping()
}
func EnsureTable(dbName string) {
conn, err := GetDB(dbName)
if err != nil {
return
}
safeName := regexp.MustCompile(`[^a-zA-Z0-9]`).ReplaceAllString(dbName, "_")
sqlCreate := fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS current.giaoban_ghichu (
id SERIAL PRIMARY KEY,
maba VARCHAR(50) NOT NULL,
mabn VARCHAR(20) NOT NULL,
loai_danhsach VARCHAR(20) NOT NULL,
ghichu TEXT,
ngay_capnhat TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unq_giaoban_item_%s UNIQUE(maba, loai_danhsach)
);
`, safeName)
_, _ = conn.Exec(sqlCreate)
}
func CleanRTF(s string) string {
if s == "" {
return ""
}
if !strings.HasPrefix(s, "{\\rtf") && !strings.Contains(s, "\\rtf") {
return s
}
cleaned := s
// Remove {\*...} blocks
re1 := regexp.MustCompile(`\{\\\*[^}]*\}`)
cleaned = re1.ReplaceAllString(cleaned, "")
// Remove general {...} groups with tags
re2 := regexp.MustCompile(`\{[^{}]*\}`)
cleaned = re2.ReplaceAllString(cleaned, "")
// Remove RTF control words (\b, \par, \fs20...)
re3 := regexp.MustCompile(`\\[a-zA-Z]+(-?\d+)? ?`)
cleaned = re3.ReplaceAllString(cleaned, " ")
// Remove stray brackets and backslashes
re4 := regexp.MustCompile(`[\\{}]`)
cleaned = re4.ReplaceAllString(cleaned, "")
// Collapse multiple spaces
re5 := regexp.MustCompile(`\s+`)
cleaned = strings.TrimSpace(re5.ReplaceAllString(cleaned, " "))
if cleaned == "" {
return "Có kết quả (Định dạng RTF)"
}
return cleaned
}