From 4ffe60112c936643c41662c1777b510f4278704c Mon Sep 17 00:00:00 2001 From: BaoXuebin Date: Wed, 6 Dec 2023 00:29:38 +0800 Subject: [PATCH] add event api --- README.md | 2 +- script/bql.go | 9 +++++ script/file.go | 56 +++++++++++++++++++++++++++++ script/paths.go | 10 ++++++ script/platform.go | 1 + server.go | 3 +- service/accounts.go | 2 +- service/events.go | 72 +++++++++++++++++++++++++++++++++++--- template/event/events.bean | 0 template/includes.bean | 4 ++- 10 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 template/event/events.bean diff --git a/README.md b/README.md index 8ed3940..16f7454 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ - [X] 投资管理(FIFO) - [X] 第三方账单导入(支付宝,微信,工商银行,农业银行) - [X] 分期记账 -- [ ] 事件 +- [X] 事件 ## 如何使用 diff --git a/script/bql.go b/script/bql.go index 8bacf05..bd51bc7 100644 --- a/script/bql.go +++ b/script/bql.go @@ -113,6 +113,15 @@ func BeanReportAllPrices(ledgerConfig *Config) string { return string(output) } +func BeanReportAllEvents(ledgerConfig *Config) string { + beanFilePath := GetLedgerEventsFilePath(ledgerConfig.DataPath) + + LogInfo(ledgerConfig.Mail, "bean-report "+beanFilePath+" events") + cmd := exec.Command("bean-report", beanFilePath, "events") + output, _ := cmd.Output() + return string(output) +} + func bqlRawQuery(ledgerConfig *Config, selectBql string, queryParamsPtr *QueryParams, queryResultPtr interface{}) (string, error) { var bql string if selectBql == "" { diff --git a/script/file.go b/script/file.go index 1471bed..727b2a8 100644 --- a/script/file.go +++ b/script/file.go @@ -1,9 +1,11 @@ package script import ( + "bufio" "io/ioutil" "os" "path/filepath" + "strings" ) func FileIfExist(filePath string) bool { @@ -59,6 +61,60 @@ func AppendFileInNewLine(filePath string, content string) error { return err } +func DeleteLinesWithText(filePath string, textToDelete string) error { + // 打开文件以供读写 + file, err := os.OpenFile(filePath, os.O_RDWR, 0644) + if err != nil { + return err + } + defer file.Close() + + // 创建一个缓冲读取器 + scanner := bufio.NewScanner(file) + + // 创建一个字符串切片,用于保存文件的每一行 + var lines []string + + // 逐行读取文件内容 + for scanner.Scan() { + line := scanner.Text() + + // 检查行是否包含要删除的文本 + if !strings.Contains(line, textToDelete) { + lines = append(lines, line) + } + } + + // 关闭文件 + file.Close() + + // 重新打开文件以供写入 + file, err = os.OpenFile(filePath, os.O_RDWR|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer file.Close() + + // 创建一个写入器 + writer := bufio.NewWriter(file) + + // 将修改后的内容写回文件 + for _, line := range lines { + _, err := writer.WriteString(line + "\n") + if err != nil { + return err + } + } + + // 刷新缓冲区,确保所有数据被写入文件 + err = writer.Flush() + if err != nil { + return err + } + + return nil +} + func CreateFile(filePath string) error { if _, e := os.Stat(filePath); os.IsNotExist(e) { _ = os.MkdirAll(filepath.Dir(filePath), os.ModePerm) diff --git a/script/paths.go b/script/paths.go index d5d2120..fc00dd7 100644 --- a/script/paths.go +++ b/script/paths.go @@ -56,3 +56,13 @@ func GetLedgerIndexFilePath(dataPath string) string { LogInfo(dataPath, dataPath+"/index.bean") return dataPath + "/index.bean" } + +func GetLedgerIncludesFilePath(dataPath string) string { + LogInfo(dataPath, dataPath+"/includes.bean") + return dataPath + "/includes.bean" +} + +func GetLedgerEventsFilePath(dataPath string) string { + LogInfo(dataPath, dataPath+"/event/events.bean") + return dataPath + "/event/events.bean" +} diff --git a/script/platform.go b/script/platform.go index 99778e3..d8fd784 100644 --- a/script/platform.go +++ b/script/platform.go @@ -6,6 +6,7 @@ import ( ) func isWindows() bool { + return false os := runtime.GOOS return os == "windows" } diff --git a/server.go b/server.go index 7541c69..af1d02f 100644 --- a/server.go +++ b/server.go @@ -84,8 +84,9 @@ func RegisterRouter(router *gin.Engine) { authorized.GET("/transaction/template", service.QueryTransactionTemplates) authorized.POST("/transaction/template", service.AddTransactionTemplate) authorized.DELETE("/transaction/template", service.DeleteTransactionTemplate) - authorized.GET("/event/page", service.GetAllEvents) + authorized.GET("/event/all", service.GetAllEvents) authorized.POST("/event", service.AddEvent) + authorized.DELETE("/event", service.DeleteEvent) authorized.GET("/tags", service.QueryTags) authorized.GET("/file/dir", service.QueryLedgerSourceFileDir) authorized.GET("/file/content", service.QueryLedgerSourceFileContent) diff --git a/service/accounts.go b/service/accounts.go index a85f52f..e111209 100644 --- a/service/accounts.go +++ b/service/accounts.go @@ -32,7 +32,7 @@ type accountPosition struct { func QueryAllAccount(c *gin.Context) { ledgerConfig := script.GetLedgerConfigFromContext(c) - bql := fmt.Sprintf("select '\\', account, '\\', sum(convert(value(position), '%s')) as market_position, '\\', sum(value(position)) as position, '\\'", ledgerConfig.OperatingCurrency) + bql := fmt.Sprintf("select '\\', account, '\\', sum(convert(value(position), '%s')) as market_position, '\\', sum(convert(value(position), currency)) as position, '\\'", ledgerConfig.OperatingCurrency) accountPositions := make([]accountPosition, 0) err := script.BQLQueryListByCustomSelect(ledgerConfig, bql, nil, &accountPositions) if err != nil { diff --git a/service/events.go b/service/events.go index 21af35a..9366f3c 100644 --- a/service/events.go +++ b/service/events.go @@ -1,17 +1,79 @@ package service -import "github.com/gin-gonic/gin" +import ( + "fmt" + "github.com/beancount-gs/script" + "github.com/gin-gonic/gin" + "strings" +) type Event struct { - Date string `json:"date"` - Type string `json:"type"` - Description string `json:"description"` + Date string `form:"date" binding:"required" json:"date"` + Type string `form:"type" binding:"required" json:"type"` + Description string `form:"description" binding:"required" json:"description"` } func GetAllEvents(c *gin.Context) { - OK(c, nil) + ledgerConfig := script.GetLedgerConfigFromContext(c) + output := script.BeanReportAllEvents(ledgerConfig) + script.LogInfo(ledgerConfig.Mail, output) + + events := make([]Event, 0) + lines := strings.Split(output, "\n") + // foreach lines + for idx, line := range lines { + if idx < 2 || idx > len(lines)-3 { + continue + } + if strings.Trim(line, " ") == "" { + continue + } + // split line by " " + words := strings.Fields(line) + events = append(events, Event{ + Date: words[0], + Type: words[1], + Description: words[2], + }) + } + OK(c, events) } func AddEvent(c *gin.Context) { + var event Event + if err := c.ShouldBindJSON(&event); err != nil { + BadRequest(c, err.Error()) + return + } + + ledgerConfig := script.GetLedgerConfigFromContext(c) + filePath := script.GetLedgerEventsFilePath(ledgerConfig.DataPath) + + line := fmt.Sprintf("%s event \"%s\" \"%s\"", event.Date, event.Type, event.Description) + // 写入文件 + err := script.AppendFileInNewLine(filePath, line) + if err != nil { + InternalError(c, err.Error()) + return + } + OK(c, event) +} + +func DeleteEvent(c *gin.Context) { + var event Event + if err := c.ShouldBindJSON(&event); err != nil { + BadRequest(c, err.Error()) + return + } + + ledgerConfig := script.GetLedgerConfigFromContext(c) + filePath := script.GetLedgerEventsFilePath(ledgerConfig.DataPath) + + line := fmt.Sprintf("%s event \"%s\" \"%s\"", event.Date, event.Type, event.Description) + err := script.DeleteLinesWithText(filePath, line) + if err != nil { + InternalError(c, err.Error()) + return + } OK(c, nil) } diff --git a/template/event/events.bean b/template/event/events.bean new file mode 100644 index 0000000..e69de29 diff --git a/template/includes.bean b/template/includes.bean index 5ca73bc..e852bd5 100644 --- a/template/includes.bean +++ b/template/includes.bean @@ -9,4 +9,6 @@ include "./price/prices.bean" ; 历史数据(用于 导入 之前的数据) include "./history.bean" ; 新的数据(按月拆分) -include "./month/months.bean" \ No newline at end of file +include "./month/months.bean" +; 事件数据 +include "./event/events.bean" \ No newline at end of file