279 lines
6.1 KiB
Go
279 lines
6.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
_ "github.com/go-sql-driver/mysql"
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
// DatabaseConfig 数据库配置
|
|
type DatabaseConfig struct {
|
|
Type string
|
|
Host string
|
|
Port string
|
|
Username string
|
|
Password string
|
|
Name string
|
|
}
|
|
|
|
// BidItem 投标项目结构体
|
|
type BidItem struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
URL string `json:"url"`
|
|
PublishDate string `json:"publishDate"`
|
|
Source string `json:"source"`
|
|
Pin bool `json:"pin"`
|
|
CreatedAt string `json:"createdAt"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
}
|
|
|
|
// AiRecommendation AI推荐结构体
|
|
type AiRecommendation struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
Confidence int `json:"confidence"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
// App struct
|
|
type App struct {
|
|
ctx context.Context
|
|
dbConfig *DatabaseConfig
|
|
projectRootDir string
|
|
}
|
|
|
|
// NewApp creates a new App application struct
|
|
func NewApp() *App {
|
|
return &App{}
|
|
}
|
|
|
|
// startup is called when the app starts. The context is saved
|
|
// so we can call the runtime methods
|
|
func (a *App) startup(ctx context.Context) {
|
|
a.ctx = ctx
|
|
a.loadEnv()
|
|
}
|
|
|
|
// loadEnv 加载 .env 文件
|
|
func (a *App) loadEnv() {
|
|
// 获取项目根目录(从当前工作目录向上查找 .env 文件)
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
fmt.Printf("获取工作目录失败: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// 查找 .env 文件
|
|
envPath := a.findEnvFile(wd)
|
|
if envPath == "" {
|
|
fmt.Println("未找到 .env 文件")
|
|
return
|
|
}
|
|
|
|
// 加载 .env 文件
|
|
if err := godotenv.Load(envPath); err != nil {
|
|
fmt.Printf("加载 .env 文件失败: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// 保存项目根目录
|
|
a.projectRootDir = filepath.Dir(envPath)
|
|
|
|
// 读取数据库配置
|
|
a.dbConfig = &DatabaseConfig{
|
|
Type: os.Getenv("DATABASE_TYPE"),
|
|
Host: os.Getenv("DATABASE_HOST"),
|
|
Port: os.Getenv("DATABASE_PORT"),
|
|
Username: os.Getenv("DATABASE_USERNAME"),
|
|
Password: os.Getenv("DATABASE_PASSWORD"),
|
|
Name: os.Getenv("DATABASE_NAME"),
|
|
}
|
|
|
|
fmt.Printf("数据库配置已加载: %s@%s:%s/%s\n", a.dbConfig.Username, a.dbConfig.Host, a.dbConfig.Port, a.dbConfig.Name)
|
|
}
|
|
|
|
// findEnvFile 查找 .env 文件
|
|
func (a *App) findEnvFile(startDir string) string {
|
|
dir := startDir
|
|
for {
|
|
envPath := filepath.Join(dir, ".env")
|
|
if _, err := os.Stat(envPath); err == nil {
|
|
return envPath
|
|
}
|
|
|
|
// 检查是否到达根目录
|
|
parent := filepath.Dir(dir)
|
|
if parent == dir {
|
|
break
|
|
}
|
|
dir = parent
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetDatabaseConfig 获取数据库配置
|
|
func (a *App) GetDatabaseConfig() *DatabaseConfig {
|
|
return a.dbConfig
|
|
}
|
|
|
|
// GetDatabaseDSN 获取数据库连接字符串
|
|
func (a *App) GetDatabaseDSN() string {
|
|
if a.dbConfig == nil {
|
|
return ""
|
|
}
|
|
|
|
// 根据数据库类型生成 DSN
|
|
switch strings.ToLower(a.dbConfig.Type) {
|
|
case "mariadb", "mysql":
|
|
return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
|
a.dbConfig.Username,
|
|
a.dbConfig.Password,
|
|
a.dbConfig.Host,
|
|
a.dbConfig.Port,
|
|
a.dbConfig.Name,
|
|
)
|
|
case "postgres", "postgresql":
|
|
return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
|
|
a.dbConfig.Host,
|
|
a.dbConfig.Port,
|
|
a.dbConfig.Username,
|
|
a.dbConfig.Password,
|
|
a.dbConfig.Name,
|
|
)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// Greet returns a greeting for the given name
|
|
func (a *App) Greet(name string) string {
|
|
return fmt.Sprintf("Hello %s, It's show time!", name)
|
|
}
|
|
|
|
// GetPinnedBidItems 获取 pin=true 的投标项目
|
|
func (a *App) GetPinnedBidItems() ([]BidItem, error) {
|
|
dsn := a.GetDatabaseDSN()
|
|
if dsn == "" {
|
|
return nil, fmt.Errorf("数据库配置未加载")
|
|
}
|
|
|
|
db, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("连接数据库失败: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// 测试连接
|
|
if err := db.Ping(); err != nil {
|
|
return nil, fmt.Errorf("数据库连接测试失败: %v", err)
|
|
}
|
|
|
|
// 查询 pin=true 的记录
|
|
query := `SELECT id, title, url, publishDate, source, pin, createdAt, updatedAt
|
|
FROM bid_items
|
|
WHERE pin = true
|
|
ORDER BY createdAt DESC`
|
|
|
|
rows, err := db.Query(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("查询失败: %v", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var items []BidItem
|
|
for rows.Next() {
|
|
var item BidItem
|
|
var publishDate, createdAt, updatedAt time.Time
|
|
|
|
err := rows.Scan(
|
|
&item.ID,
|
|
&item.Title,
|
|
&item.URL,
|
|
&publishDate,
|
|
&item.Source,
|
|
&item.Pin,
|
|
&createdAt,
|
|
&updatedAt,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("扫描行失败: %v", err)
|
|
}
|
|
|
|
item.PublishDate = publishDate.Format("2006-01-02 15:04:05")
|
|
item.CreatedAt = createdAt.Format("2006-01-02 15:04:05")
|
|
item.UpdatedAt = updatedAt.Format("2006-01-02 15:04:05")
|
|
|
|
items = append(items, item)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("遍历行失败: %v", err)
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
// GetAiRecommendations 获取 AI 推荐数据
|
|
func (a *App) GetAiRecommendations() ([]AiRecommendation, error) {
|
|
dsn := a.GetDatabaseDSN()
|
|
if dsn == "" {
|
|
return nil, fmt.Errorf("数据库配置未加载")
|
|
}
|
|
|
|
db, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("连接数据库失败: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// 测试连接
|
|
if err := db.Ping(); err != nil {
|
|
return nil, fmt.Errorf("数据库连接测试失败: %v", err)
|
|
}
|
|
|
|
// 查询 ai_recommendations 表
|
|
query := `SELECT id, title, confidence, createdAt
|
|
FROM ai_recommendations
|
|
ORDER BY createdAt DESC`
|
|
|
|
rows, err := db.Query(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("查询失败: %v", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var items []AiRecommendation
|
|
for rows.Next() {
|
|
var item AiRecommendation
|
|
var createdAt time.Time
|
|
|
|
err := rows.Scan(
|
|
&item.ID,
|
|
&item.Title,
|
|
&item.Confidence,
|
|
&createdAt,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("扫描行失败: %v", err)
|
|
}
|
|
|
|
item.CreatedAt = createdAt.Format("2006-01-02 15:04:05")
|
|
|
|
items = append(items, item)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("遍历行失败: %v", err)
|
|
}
|
|
|
|
return items, nil
|
|
}
|