add: bql exec impl and query api
This commit is contained in:
parent
34adc87098
commit
5c05aaa102
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"title": "我的账本",
|
||||
"dataPath": "D:\\beancount",
|
||||
"dataPath": "E:\\beancount",
|
||||
"operatingCurrency": "CNY",
|
||||
"startDate": "1970-01-01",
|
||||
"isBak": true
|
||||
|
|
|
|||
7
go.mod
7
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
|
||||
|
|
|
|||
4
go.sum
4
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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
12
server.go
12
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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
package service
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue