2021-11-21 14:37:13 +00:00
|
|
|
package script
|
|
|
|
|
|
|
|
|
|
import (
|
2021-11-22 08:47:49 +00:00
|
|
|
"encoding/json"
|
2021-11-21 14:37:13 +00:00
|
|
|
"fmt"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"reflect"
|
2021-11-22 14:50:10 +00:00
|
|
|
"strconv"
|
2021-11-21 14:37:13 +00:00
|
|
|
"strings"
|
2021-12-24 03:55:25 +00:00
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2021-11-21 14:37:13 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type QueryParams struct {
|
2021-12-02 14:48:45 +00:00
|
|
|
From bool `bql:"From"`
|
|
|
|
|
FromYear int `bql:"year ="`
|
|
|
|
|
FromMonth int `bql:"month ="`
|
2021-11-22 10:05:12 +00:00
|
|
|
Where bool `bql:"where"`
|
2021-11-26 09:12:07 +00:00
|
|
|
Currency string `bql:"currency ="`
|
2021-11-21 14:37:13 +00:00
|
|
|
Year int `bql:"year ="`
|
|
|
|
|
Month int `bql:"month ="`
|
2021-11-24 10:04:02 +00:00
|
|
|
Tag string `bql:"in tags"`
|
2021-11-24 09:32:24 +00:00
|
|
|
Account string `bql:"account ="`
|
2021-11-24 15:45:45 +00:00
|
|
|
AccountLike string `bql:"account ~"`
|
|
|
|
|
GroupBy string `bql:"group by"`
|
2021-11-22 10:05:12 +00:00
|
|
|
OrderBy string `bql:"order by"`
|
|
|
|
|
Limit int `bql:"limit"`
|
2021-11-23 15:33:14 +00:00
|
|
|
Path string
|
2021-11-21 14:37:13 +00:00
|
|
|
}
|
|
|
|
|
|
2021-11-22 14:50:10 +00:00
|
|
|
func GetQueryParams(c *gin.Context) QueryParams {
|
|
|
|
|
var queryParams QueryParams
|
|
|
|
|
var hasWhere bool
|
|
|
|
|
if c.Query("year") != "" {
|
|
|
|
|
val, err := strconv.Atoi(c.Query("year"))
|
|
|
|
|
if err == nil {
|
|
|
|
|
queryParams.Year = val
|
|
|
|
|
hasWhere = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if c.Query("month") != "" {
|
|
|
|
|
val, err := strconv.Atoi(c.Query("month"))
|
|
|
|
|
if err == nil {
|
|
|
|
|
queryParams.Month = val
|
|
|
|
|
hasWhere = true
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-24 10:04:02 +00:00
|
|
|
if c.Query("tag") != "" {
|
|
|
|
|
queryParams.Tag = c.Query("tag")
|
|
|
|
|
hasWhere = true
|
|
|
|
|
}
|
2021-11-22 14:50:10 +00:00
|
|
|
if c.Query("type") != "" {
|
2021-11-24 15:45:45 +00:00
|
|
|
queryParams.AccountLike = c.Query("type")
|
2021-11-22 14:50:10 +00:00
|
|
|
hasWhere = true
|
|
|
|
|
}
|
2021-11-24 09:32:24 +00:00
|
|
|
if c.Query("account") != "" {
|
|
|
|
|
queryParams.Account = c.Query("account")
|
|
|
|
|
queryParams.Limit = 100
|
|
|
|
|
hasWhere = true
|
|
|
|
|
}
|
2021-11-22 14:50:10 +00:00
|
|
|
queryParams.Where = hasWhere
|
2021-11-23 15:33:14 +00:00
|
|
|
if c.Query("path") != "" {
|
|
|
|
|
queryParams.Path = c.Query("path")
|
|
|
|
|
}
|
2021-11-22 14:50:10 +00:00
|
|
|
return queryParams
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-05 03:07:03 +00:00
|
|
|
//func BQLQueryOne(ledgerConfig *Config, queryParams *QueryParams, queryResultPtr interface{}) error {
|
|
|
|
|
// assertQueryResultIsPointer(queryResultPtr)
|
|
|
|
|
// output, err := bqlRawQuery(ledgerConfig, "", queryParams, queryResultPtr)
|
|
|
|
|
// if err != nil {
|
|
|
|
|
// return err
|
|
|
|
|
// }
|
|
|
|
|
// err = parseResult(output, queryResultPtr, true)
|
|
|
|
|
// if err != nil {
|
|
|
|
|
// return err
|
|
|
|
|
// }
|
|
|
|
|
// return nil
|
|
|
|
|
//}
|
2021-11-22 08:47:49 +00:00
|
|
|
|
|
|
|
|
func BQLQueryList(ledgerConfig *Config, queryParams *QueryParams, queryResultPtr interface{}) error {
|
|
|
|
|
assertQueryResultIsPointer(queryResultPtr)
|
2021-11-22 14:50:10 +00:00
|
|
|
output, err := bqlRawQuery(ledgerConfig, "", queryParams, queryResultPtr)
|
2021-11-22 08:47:49 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
err = parseResult(output, queryResultPtr, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-22 14:50:10 +00:00
|
|
|
func BQLQueryListByCustomSelect(ledgerConfig *Config, selectBql string, queryParams *QueryParams, queryResultPtr interface{}) error {
|
|
|
|
|
assertQueryResultIsPointer(queryResultPtr)
|
|
|
|
|
output, err := bqlRawQuery(ledgerConfig, selectBql, queryParams, queryResultPtr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2021-11-22 08:47:49 +00:00
|
|
|
}
|
2021-11-22 14:50:10 +00:00
|
|
|
err = parseResult(output, queryResultPtr, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-12 16:30:38 +00:00
|
|
|
func BeanReportAllPrices(ledgerConfig *Config) string {
|
|
|
|
|
beanFilePath := ledgerConfig.DataPath + "/index.bean"
|
|
|
|
|
|
|
|
|
|
LogInfo(ledgerConfig.Mail, "bean-report "+beanFilePath+" all_prices")
|
|
|
|
|
cmd := exec.Command("bean-report", beanFilePath, "all_prices")
|
|
|
|
|
output, _ := cmd.Output()
|
|
|
|
|
return string(output)
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-22 14:50:10 +00:00
|
|
|
func bqlRawQuery(ledgerConfig *Config, selectBql string, queryParamsPtr *QueryParams, queryResultPtr interface{}) (string, error) {
|
2021-11-25 07:41:28 +00:00
|
|
|
var bql string
|
2021-11-22 14:50:10 +00:00
|
|
|
if selectBql == "" {
|
2021-11-25 07:41:28 +00:00
|
|
|
bql = "select"
|
2021-11-22 14:50:10 +00:00
|
|
|
queryResultPtrType := reflect.TypeOf(queryResultPtr)
|
|
|
|
|
queryResultType := queryResultPtrType.Elem()
|
2021-11-22 08:47:49 +00:00
|
|
|
|
2021-11-22 14:50:10 +00:00
|
|
|
if queryResultType.Kind() == reflect.Slice {
|
|
|
|
|
queryResultType = queryResultType.Elem()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < queryResultType.NumField(); i++ {
|
|
|
|
|
typeField := queryResultType.Field(i)
|
|
|
|
|
// 字段的 tag 不带 bql 的不进行拼接
|
|
|
|
|
b := typeField.Tag.Get("bql")
|
|
|
|
|
if b != "" {
|
|
|
|
|
if strings.Contains(b, "distinct") {
|
|
|
|
|
b = strings.ReplaceAll(b, "distinct", "")
|
|
|
|
|
bql = fmt.Sprintf("%s distinct '\\', %s, ", bql, b)
|
|
|
|
|
} else {
|
|
|
|
|
bql = fmt.Sprintf("%s '\\', %s, ", bql, typeField.Tag.Get("bql"))
|
|
|
|
|
}
|
2021-11-21 14:37:13 +00:00
|
|
|
}
|
2021-11-22 08:47:49 +00:00
|
|
|
}
|
2021-11-22 14:50:10 +00:00
|
|
|
bql += " '\\'"
|
|
|
|
|
} else {
|
|
|
|
|
bql = selectBql
|
2021-11-22 08:47:49 +00:00
|
|
|
}
|
2021-11-22 14:50:10 +00:00
|
|
|
|
2021-11-22 08:47:49 +00:00
|
|
|
// 查询条件不为空时,拼接查询条件
|
|
|
|
|
if queryParamsPtr != nil {
|
|
|
|
|
queryParamsType := reflect.TypeOf(queryParamsPtr).Elem()
|
|
|
|
|
queryParamsValue := reflect.ValueOf(queryParamsPtr).Elem()
|
|
|
|
|
for i := 0; i < queryParamsType.NumField(); i++ {
|
|
|
|
|
typeField := queryParamsType.Field(i)
|
|
|
|
|
valueField := queryParamsValue.Field(i)
|
|
|
|
|
switch valueField.Kind() {
|
|
|
|
|
case reflect.String:
|
|
|
|
|
val := valueField.String()
|
|
|
|
|
if val != "" {
|
2021-11-24 15:45:45 +00:00
|
|
|
if typeField.Name == "OrderBy" || typeField.Name == "GroupBy" {
|
2021-11-22 10:05:12 +00:00
|
|
|
// 去除上一个条件后缀的 AND 关键字
|
|
|
|
|
bql = strings.Trim(bql, " AND")
|
|
|
|
|
bql = fmt.Sprintf("%s %s %s", bql, typeField.Tag.Get("bql"), val)
|
2021-11-24 10:04:02 +00:00
|
|
|
} else if typeField.Name == "Tag" {
|
|
|
|
|
bql = fmt.Sprintf("%s '%s' %s", bql, strings.Trim(val, " "), typeField.Tag.Get("bql"))
|
2021-11-22 10:05:12 +00:00
|
|
|
} else {
|
|
|
|
|
bql = fmt.Sprintf("%s %s '%s' AND", bql, typeField.Tag.Get("bql"), val)
|
|
|
|
|
}
|
2021-11-22 08:47:49 +00:00
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
case reflect.Int:
|
|
|
|
|
val := valueField.Int()
|
|
|
|
|
if val != 0 {
|
|
|
|
|
bql = fmt.Sprintf("%s %s %d AND", bql, typeField.Tag.Get("bql"), val)
|
|
|
|
|
}
|
|
|
|
|
break
|
2021-11-22 10:05:12 +00:00
|
|
|
case reflect.Bool:
|
|
|
|
|
val := valueField.Bool()
|
2021-12-02 14:48:45 +00:00
|
|
|
// where 前的 from 可能会带有 and
|
|
|
|
|
if typeField.Name == "Where" {
|
|
|
|
|
bql = strings.Trim(bql, " AND")
|
|
|
|
|
}
|
2021-11-22 10:05:12 +00:00
|
|
|
if val {
|
|
|
|
|
bql = fmt.Sprintf("%s %s ", bql, typeField.Tag.Get("bql"))
|
|
|
|
|
}
|
|
|
|
|
break
|
2021-11-22 08:47:49 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bql = strings.TrimRight(bql, " AND")
|
|
|
|
|
}
|
|
|
|
|
return queryByBQL(ledgerConfig, bql)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseResult(output string, queryResultPtr interface{}, selectOne bool) error {
|
|
|
|
|
queryResultPtrType := reflect.TypeOf(queryResultPtr)
|
|
|
|
|
queryResultType := queryResultPtrType.Elem()
|
|
|
|
|
|
|
|
|
|
if queryResultType.Kind() == reflect.Slice {
|
|
|
|
|
queryResultType = queryResultType.Elem()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lines := strings.Split(output, "\n")[2:]
|
|
|
|
|
if selectOne && len(lines) >= 3 {
|
|
|
|
|
lines = lines[2:3]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l := make([]map[string]interface{}, 0)
|
|
|
|
|
for _, line := range lines {
|
|
|
|
|
if line != "" {
|
|
|
|
|
values := strings.Split(line, "\\")
|
|
|
|
|
// 去除 '\' 分割带来的空字符串
|
|
|
|
|
values = values[1 : len(values)-1]
|
|
|
|
|
temp := make(map[string]interface{})
|
|
|
|
|
for i, val := range values {
|
|
|
|
|
field := queryResultType.Field(i)
|
|
|
|
|
jsonName := field.Tag.Get("json")
|
|
|
|
|
if jsonName == "" {
|
|
|
|
|
jsonName = field.Name
|
|
|
|
|
}
|
|
|
|
|
switch field.Type.Kind() {
|
2021-11-26 09:12:07 +00:00
|
|
|
case reflect.Int, reflect.Int32:
|
2021-11-25 07:41:28 +00:00
|
|
|
i, err := strconv.Atoi(strings.Trim(val, " "))
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
temp[jsonName] = i
|
|
|
|
|
break
|
2021-11-22 14:50:10 +00:00
|
|
|
// decimal
|
|
|
|
|
case reflect.String, reflect.Struct:
|
2021-11-22 10:05:12 +00:00
|
|
|
v := strings.Trim(val, " ")
|
|
|
|
|
if v != "" {
|
|
|
|
|
temp[jsonName] = v
|
|
|
|
|
}
|
2021-11-22 08:47:49 +00:00
|
|
|
break
|
|
|
|
|
case reflect.Array, reflect.Slice:
|
|
|
|
|
// 去除空格
|
|
|
|
|
strArray := strings.Split(val, ",")
|
|
|
|
|
notBlanks := make([]string, 0)
|
|
|
|
|
for _, s := range strArray {
|
|
|
|
|
if strings.Trim(s, " ") != "" {
|
|
|
|
|
notBlanks = append(notBlanks, s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
temp[jsonName] = notBlanks
|
|
|
|
|
break
|
|
|
|
|
default:
|
|
|
|
|
panic("Unsupported field type")
|
|
|
|
|
}
|
2021-11-21 14:37:13 +00:00
|
|
|
}
|
2021-11-22 08:47:49 +00:00
|
|
|
l = append(l, temp)
|
2021-11-21 14:37:13 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-22 08:47:49 +00:00
|
|
|
var jsonBytes []byte
|
|
|
|
|
var err error
|
|
|
|
|
if selectOne {
|
|
|
|
|
jsonBytes, err = json.Marshal(l[0])
|
|
|
|
|
} else {
|
|
|
|
|
jsonBytes, err = json.Marshal(l)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
err = json.Unmarshal(jsonBytes, queryResultPtr)
|
2021-11-21 14:37:13 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
}
|
2021-11-22 08:47:49 +00:00
|
|
|
|
|
|
|
|
func assertQueryResultIsPointer(queryResult interface{}) {
|
|
|
|
|
k := reflect.TypeOf(queryResult).Kind()
|
|
|
|
|
if k != reflect.Ptr {
|
|
|
|
|
panic("QueryResult type must be pointer, it's " + k.String())
|
|
|
|
|
}
|
|
|
|
|
}
|