diff --git a/config/config.json b/config/config.json index 1afa7ad..8d37440 100644 --- a/config/config.json +++ b/config/config.json @@ -1,6 +1,6 @@ { "title": "我的账本", - "dataPath": "D:\\beancount", + "dataPath": "E:\\beancount", "operatingCurrency": "CNY", "startDate": "1970-01-01", "isBak": true diff --git a/go.mod b/go.mod index 86bfec8..30c72ed 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,10 @@ module github.com/beancount-gs go 1.17 +require github.com/gin-gonic/gin v1.7.4 + require ( github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.7.4 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.9.0 // indirect @@ -15,8 +16,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/ugorji/go/codec v1.2.6 // indirect - golang.org/x/crypto v0.0.0-20211115234514-b4de73f9ece8 // indirect - golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c // indirect + golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect + golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 4a706d3..d47be00 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211115234514-b4de73f9ece8 h1:5QRxNnVsaJP6NAse0UdkRgL3zHMvCRRkrDVLNdNpdy4= golang.org/x/crypto v0.0.0-20211115234514-b4de73f9ece8/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= +golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -73,6 +75,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c h1:DHcbWVXeY+0Y8HHKR+rbLwnoh2F4tNCY7rTiHJ30RmA= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 h1:kwrAHlwJ0DUBZwQ238v+Uod/3eZ8B2K5rYsUHBQvzmI= +golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/script/bql.go b/script/bql.go new file mode 100644 index 0000000..b782533 --- /dev/null +++ b/script/bql.go @@ -0,0 +1,57 @@ +package script + +import ( + "fmt" + "os/exec" + "reflect" + "strings" +) + +type QueryParams struct { + Year int `bql:"year ="` + Month int `bql:"month ="` + AccountType string `bql:"account ~"` +} + +func BQLQuery(ledgerConfig *Config, queryParams QueryParams, queryResult interface{}) error { + bql := "SELECT '\\', id, '\\', date, '\\', payee, '\\', narration, '\\', account, '\\', position, '\\', tags, '\\' WHERE" + queryParamsType := reflect.TypeOf(queryParams) + queryParamsValue := reflect.ValueOf(queryParams) + for i := 0; i < queryParamsValue.NumField(); i++ { + typeField := queryParamsType.Field(i) + valueField := queryParamsValue.Field(i) + switch valueField.Kind() { + case reflect.String: + val := valueField.String() + if val != "" { + bql = fmt.Sprintf("%s %s '%s' AND", bql, typeField.Tag.Get("bql"), val) + } + case reflect.Int: + val := valueField.Int() + if val != 0 { + bql = fmt.Sprintf("%s %s %d AND", bql, typeField.Tag.Get("bql"), val) + } + } + } + bql = strings.TrimRight(bql, " AND") + + output, err := queryByBQL(ledgerConfig, bql) + if err != nil { + return err + } + + fmt.Println(output) + //panic("Unsupported result type") + return nil +} + +func queryByBQL(ledgerConfig *Config, bql string) (string, error) { + beanFilePath := ledgerConfig.DataPath + "/index.bean" + LogInfo(ledgerConfig.Mail, bql) + cmd := exec.Command("bean-query", beanFilePath, bql) + output, err := cmd.Output() + if err != nil { + return "", err + } + return string(output), nil +} diff --git a/script/config.go b/script/config.go index 59ef691..2bfc743 100644 --- a/script/config.go +++ b/script/config.go @@ -73,10 +73,10 @@ func LoadServerConfig() error { } err = json.Unmarshal(fileContent, &serverConfig) if err != nil { - LogError("Failed unmarshall config file (/config/config.json)") + LogSystemError("Failed unmarshall config file (/config/config.json)") return err } - LogInfo("Success load config file (/config/config.json)") + LogSystemInfo("Success load config file (/config/config.json)") // load white list fileContent, err = ReadFile("./config/white_list.json") if err != nil { @@ -84,10 +84,10 @@ func LoadServerConfig() error { } err = json.Unmarshal(fileContent, &whiteList) if err != nil { - LogError("Failed unmarshal whitelist file (/config/white_list.json)") + LogSystemError("Failed unmarshal whitelist file (/config/white_list.json)") return err } - LogInfo("Success load whitelist file (/config/white_list.json)") + LogSystemInfo("Success load whitelist file (/config/white_list.json)") return nil } @@ -99,10 +99,10 @@ func LoadLedgerConfigMap() error { } err = json.Unmarshal(fileContent, &ledgerConfigMap) if err != nil { - LogError("Failed unmarshal config file (" + path + ")") + LogSystemError("Failed unmarshal config file (" + path + ")") return err } - LogInfo("Success load ledger_config file (" + path + ")") + LogSystemInfo("Success load ledger_config file (" + path + ")") return nil } @@ -110,11 +110,11 @@ func WriteLedgerConfigMap(newLedgerConfigMap ConfigMap) error { path := GetServerLedgerConfigFilePath() mapBytes, err := json.Marshal(ledgerConfigMap) if err != nil { - LogError("Failed marshal ConfigMap") + LogSystemError("Failed marshal ConfigMap") return err } err = WriteFile(path, string(mapBytes)) ledgerConfigMap = newLedgerConfigMap - LogInfo("Success write ledger_config file (" + path + ")") + LogSystemInfo("Success write ledger_config file (" + path + ")") return err } diff --git a/script/file.go b/script/file.go index 42f4980..78c49f8 100644 --- a/script/file.go +++ b/script/file.go @@ -19,40 +19,40 @@ func FileIfExist(filePath string) bool { func ReadFile(filePath string) ([]byte, error) { content, err := ioutil.ReadFile(filePath) if nil != err { - LogError("Failed to read file (" + filePath + ")") + LogSystemError("Failed to read file (" + filePath + ")") return content, err } - LogInfo("Success read file (" + filePath + ")") + LogSystemInfo("Success read file (" + filePath + ")") return content, nil } func WriteFile(filePath string, content string) error { err := ioutil.WriteFile(filePath, []byte(content), os.ModePerm) if err != nil { - LogError("Failed to write file (" + filePath + ")") + LogSystemError("Failed to write file (" + filePath + ")") return err } - LogInfo("Success write file (" + filePath + ")") + LogSystemInfo("Success write file (" + filePath + ")") return nil } func CreateFile(filePath string) error { f, err := os.Create(filePath) if nil != err { - LogError(filePath + " create failed") + LogSystemError(filePath + " create failed") return err } defer f.Close() - LogInfo("Success create file " + filePath) + LogSystemInfo("Success create file " + filePath) return nil } func MkDir(dirPath string) error { err := os.MkdirAll(dirPath, os.ModePerm) if nil != err { - LogError("Failed mkdir " + dirPath) + LogSystemError("Failed mkdir " + dirPath) return err } - LogInfo("Success mkdir " + dirPath) + LogSystemInfo("Success mkdir " + dirPath) return nil } diff --git a/script/log.go b/script/log.go index bb3a6c3..e955878 100644 --- a/script/log.go +++ b/script/log.go @@ -5,10 +5,18 @@ import ( "time" ) -func LogInfo(message string) { - fmt.Printf("[Info] [%s] System: %s\n", time.Now().Format("2006-01-02 15:04:05"), message) +func LogInfo(ledgerName string, message string) { + fmt.Printf("[Info] [%s] [%s]: %s\n", time.Now().Format("2006-01-02 15:04:05"), ledgerName, message) } -func LogError(message string) { - fmt.Printf("[Error] [%s] System: %s\n", time.Now().Format("2006-01-02 15:04:05"), message) +func LogSystemInfo(message string) { + LogInfo("System", message) +} + +func LogError(ledgerName string, message string) { + fmt.Printf("[Error] [%s] [%s]: %s\n", time.Now().Format("2006-01-02 15:04:05"), ledgerName, message) +} + +func LogSystemError(message string) { + LogError("System", message) } diff --git a/server.go b/server.go index 0fb4e14..fe737b4 100644 --- a/server.go +++ b/server.go @@ -46,6 +46,10 @@ func RegisterRouter(router *gin.Engine) { { // need authorized authorized.GET("/stats/months", service.MonthsList) + authorized.GET("/transactions", service.QueryTransactions) + + // 兼容旧版本 + authorized.GET("/entry", service.QueryTransactions) } } @@ -53,19 +57,19 @@ func main() { // 读取配置文件 err := script.LoadServerConfig() if err != nil { - script.LogError("Failed to load server config, " + err.Error()) + script.LogSystemError("Failed to load server config, " + err.Error()) return } // 初始化账本文件结构 err = InitServerFiles() if err != nil { - script.LogError("Failed to init server files, " + err.Error()) + script.LogSystemError("Failed to init server files, " + err.Error()) return } // 加载缓存 err = LoadServerCache() if err != nil { - script.LogError("Failed to load server cache, " + err.Error()) + script.LogSystemError("Failed to load server cache, " + err.Error()) return } router := gin.Default() @@ -75,6 +79,6 @@ func main() { var port = ":3001" err = router.Run(port) if err != nil { - script.LogError("Failed to start server, " + err.Error()) + script.LogSystemError("Failed to start server, " + err.Error()) } } diff --git a/service/error.go b/service/error.go index c42e51b..032ca31 100644 --- a/service/error.go +++ b/service/error.go @@ -5,7 +5,7 @@ import ( "net/http" ) -func OK(c *gin.Context, data string) { +func OK(c *gin.Context, data interface{}) { c.JSON(http.StatusOK, gin.H{"code": 200, "message": "ok", "data": data}) } diff --git a/service/stats.go b/service/stats.go index 3e6fcaf..a010098 100644 --- a/service/stats.go +++ b/service/stats.go @@ -4,20 +4,28 @@ import ( "github.com/beancount-gs/script" "github.com/gin-gonic/gin" "os/exec" + "strings" ) func MonthsList(c *gin.Context) { - ledgerConfig := script.GetLedgerConfigFromContext(c) + months := make([]string, 0) + ledgerConfig := script.GetLedgerConfigFromContext(c) beanFilePath := ledgerConfig.DataPath + "/index.bean" bql := "SELECT distinct year(date), month(date)" - cmd := exec.Command("bean-query " + beanFilePath + " \"" + bql + "\"") - output, err := cmd.CombinedOutput() + cmd := exec.Command("bean-query", beanFilePath, bql) + output, err := cmd.Output() if err != nil { InternalError(c, "Failed to exec bql") return } - - script.LogInfo(string(output)) - OK(c, "") + execResult := string(output) + months = make([]string, 0) + for _, line := range strings.Split(execResult, "\n")[2:] { + if line != "" { + yearMonth := strings.Fields(line) + months = append(months, yearMonth[0]+"-"+yearMonth[1]) + } + } + OK(c, months) } diff --git a/service/transaction.go b/service/transaction.go deleted file mode 100644 index 6d43c33..0000000 --- a/service/transaction.go +++ /dev/null @@ -1 +0,0 @@ -package service diff --git a/service/transactions.go b/service/transactions.go new file mode 100644 index 0000000..abb0324 --- /dev/null +++ b/service/transactions.go @@ -0,0 +1,49 @@ +package service + +import ( + "github.com/beancount-gs/script" + "github.com/gin-gonic/gin" + "strconv" +) + +type Transactions struct { + Id string `bql:"id"` + Date string `bql:"date"` + payee string + narration string + account string + position string + tags string +} + +func getQueryModel(c *gin.Context) script.QueryParams { + var queryParams script.QueryParams + if c.Query("year") != "" { + val, err := strconv.Atoi(c.Query("year")) + if err == nil { + queryParams.Year = val + } + } + if c.Query("month") != "" { + val, err := strconv.Atoi(c.Query("month")) + if err == nil { + queryParams.Month = val + } + } + if c.Query("type") != "" { + queryParams.AccountType = c.Query("type") + } + return queryParams +} + +func QueryTransactions(c *gin.Context) { + ledgerConfig := script.GetLedgerConfigFromContext(c) + queryParams := getQueryModel(c) + transactions := make([]Transactions, 0) + err := script.BQLQuery(ledgerConfig, queryParams, transactions) + if err != nil { + InternalError(c, err.Error()) + return + } + OK(c, transactions) +} diff --git a/tests/main.go b/tests/main.go index 535e096..edb936c 100644 --- a/tests/main.go +++ b/tests/main.go @@ -1,25 +1,17 @@ package tests -import "fmt" +import ( + "fmt" + "reflect" +) type Student struct { - name string + Name string } -func say(student Student) *Student { - return &student -} - -func say2(student Student) Student { - return student -} - -func Test() { - str := "Hello~" - fmt.Println(str) - fmt.Println(&str) - - student := Student{name: "Bao"} - fmt.Println(student) - fmt.Println(&student) +func Test(i interface{}) { + t := reflect.TypeOf(i) + k := t.Kind() + v := reflect.ValueOf(i) + fmt.Printf("type: %s, kind: %s, value: %s", t, k, v) }