157 lines
3.4 KiB
Go
157 lines
3.4 KiB
Go
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
|
||
|
|
}
|