electricity_bill_calc_service/main.go

194 lines
5.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"database/sql"
"electricity_bill_calc/cache"
"electricity_bill_calc/config"
"electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/migration"
"electricity_bill_calc/model"
"electricity_bill_calc/router"
"electricity_bill_calc/service"
"encoding/csv"
"fmt"
"io"
"os"
"strconv"
"time"
jsontime "github.com/liamylian/jsontime/v2/v2"
"github.com/samber/lo"
"github.com/shopspring/decimal"
"github.com/uptrace/bun/migrate"
"go.uber.org/zap"
)
func init() {
l := logger.Named("Init")
err := config.SetupSetting()
if err != nil {
l.Fatal("Configuration load failed.", zap.Error(err))
}
l.Info("Configuration loaded!")
err = global.SetupDatabaseConnection()
if err != nil {
l.Fatal("Main Database connect failed.", zap.Error(err))
}
l.Info("Main Database connected!")
migrator := migrate.NewMigrator(global.DB, migration.Migrations)
ctx, cancel := global.TimeoutContext(12)
defer cancel()
err = migrator.Init(ctx)
if err != nil {
l.Fatal("Database migration unable to initialized.", zap.Error(err))
}
group, err := migrator.Migrate(ctx)
if err != nil {
l.Fatal("Database migrate failed.", zap.Error(err))
}
if group.IsZero() {
l.Info("There are no new migrations to run (database is up to date)")
}
err = global.SetupRedisConnection()
if err != nil {
l.Fatal("Main Cache Database connect failed.", zap.Error(err))
}
l.Info("Main Cache Database connected!")
err = initializeRegions()
if err != nil {
l.Fatal("Regions initialize failed.", zap.Error(err))
}
l.Info("Regions synchronized.")
err = intializeSingularity()
if err != nil {
l.Fatal("Singularity account intialize failed.", zap.Error(err))
}
l.Info("Singularity account intialized.")
timeZoneShanghai, _ := time.LoadLocation("Asia/Shanghai")
jsontime.AddTimeFormatAlias("simple_datetime", "2006-01-02 15:04:05")
jsontime.AddTimeFormatAlias("simple_date", "2006-01-02")
jsontime.AddLocaleAlias("shanghai", timeZoneShanghai)
}
func initializeRegions() error {
ctx, cancel := global.TimeoutContext()
defer cancel()
logger.Info("Synchronize regions...")
regionCsvFile, err := os.Open("regions.csv")
if err != nil {
return fmt.Errorf("region initialize file is not found: %w", err)
}
defer regionCsvFile.Close()
var existRegions = make([]string, 0)
err = global.DB.NewSelect().Model((*model.Region)(nil)).
Column("code").
Scan(ctx, &existRegions)
if err != nil {
return fmt.Errorf("unable to retreive regions from database: %w", err)
}
regionCsv := csv.NewReader(regionCsvFile)
transaction, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return fmt.Errorf("unable to intiate database transaction: %w", err)
}
for {
record, err := regionCsv.Read()
if err == io.EOF {
break
}
if lo.Contains(existRegions, record[0]) {
continue
}
level, err := strconv.Atoi(record[2])
if err != nil {
continue
}
if _, err := transaction.NewInsert().Model(&model.Region{
Code: record[0],
Name: record[1],
Level: level,
Parent: record[3],
}).Exec(ctx); err != nil {
return fmt.Errorf("region synchronize in failed: %v, %w", record, err)
}
}
if err = transaction.Commit(); err != nil {
return fmt.Errorf("synchronize regions to database failed: %w", err)
}
return nil
}
func intializeSingularity() error {
singularityExists, err := service.UserService.IsUserExists("000")
if err != nil {
return fmt.Errorf("singularity detect failed: %w", err)
}
if singularityExists {
return nil
}
singularity := &model.User{
Id: "000",
Username: "singularity",
Type: 2,
Enabled: true,
}
singularityName := "Singularity"
singularityExpires, err := model.ParseDate("2099-12-31")
if err != nil {
return fmt.Errorf("singularity expires time parse failed: %w", err)
}
singularityDetail := &model.UserDetail{
Name: &singularityName,
UnitServiceFee: decimal.Zero,
ServiceExpiration: singularityExpires,
}
verifyCode, err := service.UserService.CreateUser(singularity, singularityDetail)
if err != nil {
return fmt.Errorf("singularity account failed to create: %w", err)
}
logger.Info(
fmt.Sprintf("Singularity account created, use %s as verify code to reset password.", verifyCode),
zap.String("account", "singularity"),
zap.String("verifyCode", verifyCode),
)
return nil
}
func DBConnectionKeepLive() {
for range time.Tick(30 * time.Second) {
err := global.DB.Ping()
if err != nil {
continue
}
}
}
func RedisOrphanCleanup() {
cleanLogger := logger.Named("Cache").With(zap.String("function", "Cleanup"))
for range time.Tick(2 * time.Minute) {
cleanLogger.Info("Proceeding cleanup orphan keys.")
err := cache.ClearOrphanRelationItems()
if err != nil {
cleanLogger.Error("Orphan keys clear failed.")
continue
}
}
}
func main() {
// 本次停用检测的原因是使用Ping来保持数据库链接看起来没有什么用处。
// go DBConnectionKeepLive()
go RedisOrphanCleanup()
app := router.App()
app.Listen(fmt.Sprintf(":%d", config.ServerSettings.HttpPort))
}