Compare commits
180 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 25edb6b5b7 | |||
| ead64dc776 | |||
| 876e3b1f45 | |||
| d6e940803d | |||
| 203e72f8a2 | |||
| 541c82410e | |||
| b680112fdb | |||
| 4ab5ab9ff4 | |||
| b425024359 | |||
| 78337c14ec | |||
| cdba8af129 | |||
| 9a7426180d | |||
| b7131603c4 | |||
| 2f3a5a0a59 | |||
| c2468a7389 | |||
| b6ba3486d6 | |||
| f59e909d30 | |||
| 3bb47de45a | |||
| c71973850e | |||
| 65f062b860 | |||
| 7b502fed36 | |||
| fd6b15bdaf | |||
| f31bf24ec2 | |||
| 30edb4d9fa | |||
| a32c3a1589 | |||
| 127e027940 | |||
| 2ee0838d8a | |||
| 72c23499f2 | |||
| 96d8868694 | |||
| 1533a57cb8 | |||
| 615f52d2e4 | |||
| f7dfd4ec77 | |||
| 2276aa12ef | |||
| 6505594fd0 | |||
| c55e26021b | |||
| 78420a692f | |||
| 6d9af149ad | |||
| 253cad35ef | |||
| 47c8736f34 | |||
| dcce8ace9e | |||
| 125ccc5c3b | |||
| 432d59d483 | |||
| 60f222b011 | |||
| 7a8b86ed12 | |||
| b940afa6b0 | |||
| 8afd00ff86 | |||
| 8992fd6d3a | |||
| 524a892480 | |||
| c420e23edb | |||
| 7419e03d67 | |||
| 9425544d1c | |||
| a8d78832df | |||
| adfb2236dc | |||
| 6034598bb4 | |||
| 6fe44cb1cb | |||
| 39d67d1775 | |||
| a8f9f12bb8 | |||
| 2c1abb1d11 | |||
| 21e0219568 | |||
| 952c3a1243 | |||
| 33cd7ebd6b | |||
| be02b3418d | |||
| f390617167 | |||
| f7410200e3 | |||
| 9cfdfbac78 | |||
| b015e78fa0 | |||
| bb9092c3e5 | |||
| cc1bad8d3f | |||
| 8f7380d796 | |||
| f37b9aee70 | |||
| 1ab9027c93 | |||
| 96d341aaab | |||
| 1f25f511cc | |||
| 07c7a628d1 | |||
| d7a59845eb | |||
| 98619083a1 | |||
| c8a9038efe | |||
| b638d8bd65 | |||
| 77753a123f | |||
| 2c9d475a45 | |||
| 3540209b26 | |||
| 797681d76c | |||
| 0ec83f9a95 | |||
| ec798bd624 | |||
| 6b57201e59 | |||
| 4e35dfe6ed | |||
| 970bdb0157 | |||
| d656faf915 | |||
| 2ecc70288e | |||
| ce95cdc021 | |||
| 22130c7afe | |||
| d85c65faef | |||
| c9f33eefec | |||
| 1826e76a34 | |||
| b9a6b7af22 | |||
| 95bfea4feb | |||
| e4bb8ac94a | |||
| d7a49bf56d | |||
| 3d1a1670cc | |||
| 7de6a79611 | |||
| 83198b39cb | |||
| db0724f09b | |||
| c1b013c926 | |||
| 40bc84ea3d | |||
| e977a97013 | |||
| 9330f7f5ea | |||
| d20bcc162c | |||
| b48ad793da | |||
| 3754734130 | |||
| 918b88332b | |||
| 9ba182d65c | |||
| 8cf54ce25b | |||
| 22d00739ff | |||
| 3b2a317d2b | |||
| afa9b173ba | |||
| 53f24c033c | |||
| 1ffcaaef8e | |||
| 62ba00270a | |||
| fe31335cf5 | |||
| b709b58fef | |||
| 76b220aa5d | |||
| dbf8f91696 | |||
| 335e8730da | |||
| cbed318832 | |||
| 31a6568cbe | |||
| 2aa86361e1 | |||
| 2a21dad90a | |||
| bae9f60012 | |||
| 79d1189803 | |||
| c3d0c8921b | |||
| 1da98a058b | |||
| 152386c5a0 | |||
| 5e2845d3a1 | |||
| bab49c9a66 | |||
| 1d3bfcaee5 | |||
| a37a33321f | |||
| 48042ecce4 | |||
| 5d8227d8bb | |||
| 38d3d77c59 | |||
| fec195b3a1 | |||
| 5945ed64c6 | |||
| ce515d991a | |||
| cb04dc6c34 | |||
| 17905c2d21 | |||
| b7c243823a | |||
| ebea14c74f | |||
| d6ef790010 | |||
| 5c64580378 | |||
| e49164fa81 | |||
| 8265e24add | |||
| 283985df52 | |||
| 95adca04bd | |||
| 2e65138222 | |||
| 87039524a7 | |||
| 66cd21c35f | |||
| 7e64131931 | |||
| e30d40cfbb | |||
| 751ed0003d | |||
| 6d9f89a1d4 | |||
| bc329be03b | |||
| 4597a0a50d | |||
| 3dda86d170 | |||
| d847da7591 | |||
| e1eca90835 | |||
| 9283b8c284 | |||
| d8d9afa4d2 | |||
| 4d3829c345 | |||
| a23037a437 | |||
| 181295e54e | |||
| 8cc2499e21 | |||
| a2c49452b4 | |||
| ebf0df5ca8 | |||
| 73bf691ccc | |||
| b2e9701826 | |||
| 789b0a14d1 | |||
| 3ee64fcd8c | |||
| 527503b0d9 | |||
| a95b2fccd7 | |||
| 7e2abb43e3 | |||
| 48fd753a96 |
+3
-1
@@ -1,2 +1,4 @@
|
||||
/.idea/*
|
||||
.idea
|
||||
.idea
|
||||
/example/config/app.json
|
||||
/example/tpt/demo/
|
||||
|
||||
@@ -7,17 +7,17 @@ AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
|
||||
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution
|
||||
as defined by Sections 1 through 9 of that document.
|
||||
|
||||
|
||||
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
|
||||
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
@@ -26,31 +26,31 @@ or indirect, to cause the direction or management of such entity, whether
|
||||
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
|
||||
of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
|
||||
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
|
||||
granted by that License.
|
||||
|
||||
|
||||
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
|
||||
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation
|
||||
or translation of a Source form, including but not limited to compiled object
|
||||
code, generated documentation, and conversions to other media types.
|
||||
|
||||
|
||||
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form,
|
||||
made available under the License, as indicated by a copyright notice that
|
||||
is included in or attached to the work (an example is provided in the Appendix
|
||||
below).
|
||||
|
||||
|
||||
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form,
|
||||
that is based on (or derived from) the Work and for which the editorial revisions,
|
||||
@@ -59,7 +59,7 @@ original work of authorship. For the purposes of that License, Derivative
|
||||
Works shall not include works that remain separable from, or merely link (or
|
||||
bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
|
||||
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative
|
||||
@@ -74,7 +74,7 @@ for the purpose of discussing and improving the Work, but excluding communicatio
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
|
||||
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently incorporated
|
||||
@@ -142,7 +142,7 @@ any additional terms or conditions. Notwithstanding the above, nothing herein
|
||||
shall supersede or modify the terms of any separate license agreement you
|
||||
may have executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names,
|
||||
6. Trademarks. that License does not grant permission to use the trade names,
|
||||
trademarks, service marks, or product names of the Licensor, except as required
|
||||
for reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
+134
-108
@@ -1,14 +1,14 @@
|
||||
package hotime
|
||||
|
||||
import (
|
||||
. "./cache"
|
||||
"./code"
|
||||
. "./common"
|
||||
. "./db"
|
||||
. "./log"
|
||||
. "code.hoteas.com/golang/hotime/cache"
|
||||
"code.hoteas.com/golang/hotime/code"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
. "code.hoteas.com/golang/hotime/db"
|
||||
. "code.hoteas.com/golang/hotime/log"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -20,17 +20,15 @@ import (
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
*code.MakeCode
|
||||
MakeCodeRouter Router
|
||||
MakeCodeRouter map[string]*code.MakeCode
|
||||
MethodRouter
|
||||
Router
|
||||
ContextBase
|
||||
Error
|
||||
Log *logrus.Logger
|
||||
WebConnectLog *logrus.Logger
|
||||
Port string //端口号
|
||||
TLSPort string //ssl访问端口号
|
||||
connectListener []func(this *Context) bool //所有的访问监听,true按原计划继续使用,false表示有监听器处理
|
||||
connectListener []func(that *Context) bool //所有的访问监听,true按原计划继续使用,false表示有监听器处理
|
||||
connectDbFunc func(err ...*Error) (master, slave *sql.DB)
|
||||
configPath string
|
||||
Config Map
|
||||
@@ -71,7 +69,21 @@ func (that *Application) Run(router Router) {
|
||||
that.Router = Router{}
|
||||
}
|
||||
for k, v := range router {
|
||||
that.Router[k] = v
|
||||
if that.Router[k] == nil {
|
||||
that.Router[k] = v
|
||||
}
|
||||
//直达接口层复用
|
||||
for k1, v1 := range v {
|
||||
if that.Router[k][k1] == nil {
|
||||
that.Router[k][k1] = v1
|
||||
}
|
||||
|
||||
for k2, v2 := range v1 {
|
||||
|
||||
that.Router[k][k1][k2] = v2
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//重新设置MethodRouter//直达路由
|
||||
that.MethodRouter = MethodRouter{}
|
||||
@@ -225,10 +237,8 @@ func (that *Application) SetConfig(configPath ...string) {
|
||||
|
||||
}
|
||||
|
||||
that.Log = GetLog(that.Config.GetString("logFile"), true)
|
||||
that.Error = Error{Logger: that.Log}
|
||||
if that.Config.Get("webConnectLogShow") == nil || that.Config.GetBool("webConnectLogShow") {
|
||||
that.WebConnectLog = GetLog(that.Config.GetString("webConnectLogFile"), false)
|
||||
if that.Error.GetError() != nil {
|
||||
fmt.Println(that.Error.GetError().Error())
|
||||
}
|
||||
|
||||
//文件如果损坏则不写入配置防止配置文件数据丢失
|
||||
@@ -255,15 +265,21 @@ func (that *Application) SetConfig(configPath ...string) {
|
||||
|
||||
}
|
||||
|
||||
that.Log = GetLog(that.Config.GetString("logFile"), true)
|
||||
that.Error = Error{Logger: that.Log}
|
||||
if that.Config.Get("webConnectLogShow") == nil || that.Config.GetBool("webConnectLogShow") {
|
||||
that.WebConnectLog = GetLog(that.Config.GetString("webConnectLogFile"), false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// SetConnectListener 连接判断,返回true继续传输至控制层,false则停止传输
|
||||
func (that *Application) SetConnectListener(lis func(this *Context) bool) {
|
||||
// SetConnectListener 连接判断,返回false继续传输至控制层,true则停止传输
|
||||
func (that *Application) SetConnectListener(lis func(that *Context) (isFinished bool)) {
|
||||
that.connectListener = append(that.connectListener, lis)
|
||||
}
|
||||
|
||||
//网络错误
|
||||
//func (this *Application) session(w http.ResponseWriter, req *http.Request) {
|
||||
//func (that *Application) session(w http.ResponseWriter, req *http.Request) {
|
||||
//
|
||||
//}
|
||||
|
||||
@@ -341,14 +357,18 @@ func (that *Application) handler(w http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
//是否展示日志
|
||||
if that.WebConnectLog != nil {
|
||||
ipStr := Substr(context.Req.RemoteAddr, 0, strings.Index(context.Req.RemoteAddr, ":"))
|
||||
|
||||
//负载均衡优化
|
||||
if ipStr == "127.0.0.1" {
|
||||
if req.Header.Get("X-Forwarded-For") != "" {
|
||||
ipStr = req.Header.Get("X-Forwarded-For")
|
||||
} else if req.Header.Get("X-Real-IP") != "" {
|
||||
ipStr = req.Header.Get("X-Real-IP")
|
||||
}
|
||||
ipStr := ""
|
||||
if req.Header.Get("X-Forwarded-For") != "" {
|
||||
ipStr = req.Header.Get("X-Forwarded-For")
|
||||
} else if req.Header.Get("X-Real-IP") != "" {
|
||||
ipStr = req.Header.Get("X-Real-IP")
|
||||
}
|
||||
//负载均衡优化
|
||||
if ipStr == "" {
|
||||
//RemoteAddr := that.Req.RemoteAddr
|
||||
ipStr = Substr(context.Req.RemoteAddr, 0, strings.Index(context.Req.RemoteAddr, ":"))
|
||||
}
|
||||
|
||||
that.WebConnectLog.Infoln(ipStr, context.Req.Method,
|
||||
@@ -358,16 +378,17 @@ func (that *Application) handler(w http.ResponseWriter, req *http.Request) {
|
||||
}()
|
||||
|
||||
//访问拦截true继续false暂停
|
||||
connectListenerLen := len(that.connectListener)
|
||||
if connectListenerLen != 0 {
|
||||
for i := 0; i < connectListenerLen; i++ {
|
||||
connectListenerLen := len(that.connectListener) - 1
|
||||
|
||||
if !that.connectListener[i](&context) {
|
||||
|
||||
context.View()
|
||||
return
|
||||
}
|
||||
for true {
|
||||
if connectListenerLen < 0 {
|
||||
break
|
||||
}
|
||||
if that.connectListener[connectListenerLen](&context) {
|
||||
context.View()
|
||||
return
|
||||
}
|
||||
connectListenerLen--
|
||||
}
|
||||
|
||||
//接口服务
|
||||
@@ -420,8 +441,14 @@ func (that *Application) handler(w http.ResponseWriter, req *http.Request) {
|
||||
header.Set("Cache-Control", "no-cache")
|
||||
}
|
||||
|
||||
if strings.Index(path, ".m3u8") != -1 {
|
||||
header.Add("Content-Type", "audio/mpegurl")
|
||||
t := strings.LastIndex(path, ".")
|
||||
if t != -1 {
|
||||
tt := path[t:]
|
||||
|
||||
if MimeMaps[tt] != "" {
|
||||
|
||||
header.Add("Content-Type", MimeMaps[tt])
|
||||
}
|
||||
}
|
||||
|
||||
//w.Write(data)
|
||||
@@ -520,40 +547,59 @@ func (that *Application) crossDomain(context *Context, sessionId string) {
|
||||
}
|
||||
|
||||
//Init 初始化application
|
||||
func Init(config string) Application {
|
||||
func Init(config string) *Application {
|
||||
appIns := Application{}
|
||||
//手动模式,
|
||||
appIns.SetConfig(config)
|
||||
|
||||
SetDB(&appIns)
|
||||
appIns.SetCache()
|
||||
appIns.MakeCode = &code.MakeCode{}
|
||||
codeConfig := appIns.Config.GetMap("codeConfig")
|
||||
codeConfig := appIns.Config.GetSlice("codeConfig")
|
||||
|
||||
if codeConfig != nil {
|
||||
|
||||
for k, _ := range codeConfig {
|
||||
if appIns.Config.GetInt("mode") == 2 {
|
||||
appIns.MakeCode.Db2JSON(k, codeConfig.GetString(k), &appIns.Db, true)
|
||||
} else if appIns.Config.GetInt("mode") == 3 {
|
||||
appIns.MakeCode.Db2JSON(k, codeConfig.GetString(k), &appIns.Db, false)
|
||||
} else {
|
||||
appIns.MakeCode.Db2JSON(k, codeConfig.GetString(k), nil, false)
|
||||
codeMake := codeConfig.GetMap(k)
|
||||
if codeMake == nil {
|
||||
continue
|
||||
}
|
||||
//codeMake["table"] = k
|
||||
if appIns.MakeCodeRouter == nil {
|
||||
appIns.MakeCodeRouter = map[string]*code.MakeCode{}
|
||||
}
|
||||
|
||||
if codeMake.GetString("name") == "" {
|
||||
codeMake["name"] = codeMake.GetString("table")
|
||||
}
|
||||
|
||||
appIns.MakeCodeRouter[codeMake.GetString("name")] = &code.MakeCode{Error: appIns.Error}
|
||||
appIns.MakeCodeRouter[codeMake.GetString("name")].Db2JSON(&appIns.Db, codeMake)
|
||||
|
||||
//接入动态代码层
|
||||
if appIns.Router == nil {
|
||||
appIns.Router = Router{}
|
||||
}
|
||||
appIns.Router[k] = TptProject
|
||||
for k1, _ := range appIns.MakeCode.TableColumns {
|
||||
appIns.Router[k][k1] = appIns.Router[k]["hotimeCommon"]
|
||||
|
||||
//appIns.Router[codeMake.GetString("name")] = TptProject
|
||||
appIns.Router[codeMake.GetString("name")] = Proj{}
|
||||
for k2, _ := range TptProject {
|
||||
appIns.Router[codeMake.GetString("name")][k2] = Ctr{}
|
||||
for k3, v3 := range TptProject[k2] {
|
||||
appIns.Router[codeMake.GetString("name")][k2][k3] = v3
|
||||
}
|
||||
}
|
||||
|
||||
setMakeCodeLintener(k, &appIns)
|
||||
for k1, _ := range appIns.MakeCodeRouter[codeMake.GetString("name")].TableColumns {
|
||||
appIns.Router[codeMake.GetString("name")][k1] = appIns.Router[codeMake.GetString("name")]["hotimeCommon"]
|
||||
}
|
||||
|
||||
setMakeCodeListener(codeMake.GetString("name"), &appIns)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return appIns
|
||||
return &appIns
|
||||
}
|
||||
|
||||
// SetDB 智能数据库设置
|
||||
@@ -573,6 +619,8 @@ func SetMysqlDB(appIns *Application, config Map) {
|
||||
appIns.Db.Type = "mysql"
|
||||
appIns.Db.DBName = config.GetString("name")
|
||||
appIns.Db.Prefix = config.GetString("prefix")
|
||||
appIns.Db.Log = appIns.Log
|
||||
appIns.Db.Mode = appIns.Config.GetCeilInt("mode")
|
||||
appIns.SetConnectDB(func(err ...*Error) (master, slave *sql.DB) {
|
||||
//master数据库配置
|
||||
query := config.GetString("user") + ":" + config.GetString("password") +
|
||||
@@ -602,6 +650,8 @@ func SetSqliteDB(appIns *Application, config Map) {
|
||||
|
||||
appIns.Db.Type = "sqlite"
|
||||
appIns.Db.Prefix = config.GetString("prefix")
|
||||
appIns.Db.Mode = appIns.Config.GetCeilInt("mode")
|
||||
appIns.Db.Log = appIns.Log
|
||||
appIns.SetConnectDB(func(err ...*Error) (master, slave *sql.DB) {
|
||||
db, e := sql.Open("sqlite3", config.GetString("path"))
|
||||
if e != nil && len(err) != 0 {
|
||||
@@ -613,81 +663,56 @@ func SetSqliteDB(appIns *Application, config Map) {
|
||||
})
|
||||
}
|
||||
|
||||
func setMakeCodeLintener(name string, appIns *Application) {
|
||||
appIns.SetConnectListener(func(context *Context) bool {
|
||||
if len(context.RouterString) > 1 && context.RouterString[0] == name {
|
||||
if context.RouterString[1] == "hotime" && context.RouterString[2] == "login" {
|
||||
return true
|
||||
}
|
||||
if context.RouterString[1] == "hotime" && context.RouterString[2] == "logout" {
|
||||
return true
|
||||
}
|
||||
func setMakeCodeListener(name string, appIns *Application) {
|
||||
appIns.SetConnectListener(func(context *Context) (isFinished bool) {
|
||||
|
||||
if context.Session(name+"_id").Data == nil {
|
||||
context.Display(2, "你还没有登录")
|
||||
return false
|
||||
}
|
||||
codeIns := appIns.MakeCodeRouter[name]
|
||||
|
||||
if len(context.RouterString) < 2 || appIns.MakeCodeRouter[context.RouterString[0]] == nil {
|
||||
return isFinished
|
||||
}
|
||||
|
||||
//文件上传接口
|
||||
if len(context.RouterString) == 1 && context.RouterString[0] == "file" && context.Req.Method == "POST" {
|
||||
if context.Session(name+"_id").Data == nil {
|
||||
if len(context.RouterString) > 1 && context.RouterString[0] == name {
|
||||
if context.RouterString[1] == "hotime" && context.RouterString[2] == "login" {
|
||||
return isFinished
|
||||
}
|
||||
if context.RouterString[1] == "hotime" && context.RouterString[2] == "logout" {
|
||||
return isFinished
|
||||
}
|
||||
|
||||
if context.RouterString[1] == "hotime" && context.RouterString[2] == "config" {
|
||||
return isFinished
|
||||
}
|
||||
if context.RouterString[1] == "hotime" && context.RouterString[2] == "wallpaper" {
|
||||
return isFinished
|
||||
}
|
||||
if context.Session(codeIns.FileConfig.GetString("table")+"_id").Data == nil {
|
||||
context.Display(2, "你还没有登录")
|
||||
return false
|
||||
return true
|
||||
}
|
||||
//读取网络文件
|
||||
fi, fheader, err := context.Req.FormFile("file")
|
||||
if err != nil {
|
||||
context.Display(3, err)
|
||||
return false
|
||||
|
||||
}
|
||||
filePath := context.Config.GetString("filePath")
|
||||
if filePath == "" {
|
||||
filePath = "file/2006/01/02/"
|
||||
}
|
||||
|
||||
path := time.Now().Format(filePath)
|
||||
e := os.MkdirAll(context.Config.GetString("tpt")+"/"+path, os.ModeDir)
|
||||
if e != nil {
|
||||
context.Display(3, e)
|
||||
return false
|
||||
}
|
||||
filePath = path + Md5(ObjToStr(RandX(100000, 9999999))) + fheader.Filename[strings.LastIndex(fheader.Filename, "."):]
|
||||
newFile, e := os.Create(context.Config.GetString("tpt") + "/" + filePath)
|
||||
|
||||
if e != nil {
|
||||
context.Display(3, e)
|
||||
return false
|
||||
}
|
||||
|
||||
_, e = io.Copy(newFile, fi)
|
||||
|
||||
if e != nil {
|
||||
context.Display(3, e)
|
||||
return false
|
||||
}
|
||||
|
||||
context.Display(0, filePath)
|
||||
return false
|
||||
}
|
||||
|
||||
if len(context.RouterString) < 2 || len(context.RouterString) > 3 ||
|
||||
!(context.Router[context.RouterString[0]] != nil &&
|
||||
context.Router[context.RouterString[0]][context.RouterString[1]] != nil) {
|
||||
return true
|
||||
return isFinished
|
||||
}
|
||||
//排除无效操作
|
||||
if len(context.RouterString) == 2 &&
|
||||
context.Req.Method != "GET" &&
|
||||
context.Req.Method != "POST" {
|
||||
return true
|
||||
return isFinished
|
||||
}
|
||||
//排除已有接口的无效操作
|
||||
if len(context.RouterString) == 3 && context.Router[context.RouterString[0]] != nil && context.Router[context.RouterString[0]][context.RouterString[1]] != nil && context.Router[context.RouterString[0]][context.RouterString[1]][context.RouterString[2]] != nil {
|
||||
return isFinished
|
||||
}
|
||||
|
||||
//列表检索
|
||||
if len(context.RouterString) == 2 &&
|
||||
context.Req.Method == "GET" {
|
||||
if context.Router[context.RouterString[0]][context.RouterString[1]]["search"] == nil {
|
||||
return true
|
||||
return isFinished
|
||||
}
|
||||
context.Router[context.RouterString[0]][context.RouterString[1]]["search"](context)
|
||||
}
|
||||
@@ -701,14 +726,14 @@ func setMakeCodeLintener(name string, appIns *Application) {
|
||||
}
|
||||
if len(context.RouterString) == 3 &&
|
||||
context.Req.Method == "POST" {
|
||||
return true
|
||||
return isFinished
|
||||
}
|
||||
//查询单条
|
||||
if len(context.RouterString) == 3 &&
|
||||
context.Req.Method == "GET" {
|
||||
|
||||
if context.Router[context.RouterString[0]][context.RouterString[1]]["info"] == nil {
|
||||
return true
|
||||
return isFinished
|
||||
}
|
||||
|
||||
context.Router[context.RouterString[0]][context.RouterString[1]]["info"](context)
|
||||
@@ -733,7 +758,8 @@ func setMakeCodeLintener(name string, appIns *Application) {
|
||||
|
||||
context.Router[context.RouterString[0]][context.RouterString[1]]["remove"](context)
|
||||
}
|
||||
context.View()
|
||||
return false
|
||||
|
||||
//context.View()
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
. "../common"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"errors"
|
||||
)
|
||||
|
||||
|
||||
Vendored
+14
-14
@@ -1,7 +1,7 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
. "../common"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
@@ -30,14 +30,14 @@ type CacheDb struct {
|
||||
isInit bool
|
||||
}
|
||||
|
||||
func (this *CacheDb) GetError() *Error {
|
||||
func (that *CacheDb) GetError() *Error {
|
||||
|
||||
return this.Error
|
||||
return that.Error
|
||||
|
||||
}
|
||||
|
||||
func (this *CacheDb) SetError(err *Error) {
|
||||
this.Error = err
|
||||
func (that *CacheDb) SetError(err *Error) {
|
||||
that.Error = err
|
||||
}
|
||||
|
||||
func (that *CacheDb) initDbTable() {
|
||||
@@ -58,7 +58,7 @@ func (that *CacheDb) initDbTable() {
|
||||
return
|
||||
}
|
||||
|
||||
_, e := that.Db.Exec("CREATE TABLE `" + that.Db.GetPrefix() + "cached` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `ckey` varchar(60) DEFAULT NULL, `cvalue` varchar(2000) DEFAULT NULL, `time` bigint(20) DEFAULT NULL, `endtime` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=198740 DEFAULT CHARSET=utf8")
|
||||
_, e := that.Db.Exec("CREATE TABLE `" + that.Db.GetPrefix() + "cached` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `key` varchar(60) DEFAULT NULL, `value` varchar(2000) DEFAULT NULL, `time` bigint(20) DEFAULT NULL, `endtime` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=198740 DEFAULT CHARSET=utf8")
|
||||
if e.GetError() == nil {
|
||||
that.isInit = true
|
||||
}
|
||||
@@ -74,8 +74,8 @@ func (that *CacheDb) initDbTable() {
|
||||
}
|
||||
_, e := that.Db.Exec(`CREATE TABLE "` + that.Db.GetPrefix() + `cached" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"ckey" TEXT(60),
|
||||
"cvalue" TEXT(2000),
|
||||
"key" TEXT(60),
|
||||
"value" TEXT(2000),
|
||||
"time" integer,
|
||||
"endtime" integer
|
||||
);`)
|
||||
@@ -90,7 +90,7 @@ func (that *CacheDb) initDbTable() {
|
||||
//获取Cache键只能为string类型
|
||||
func (that *CacheDb) get(key string) interface{} {
|
||||
|
||||
cached := that.Db.Get("cached", "*", Map{"ckey": key})
|
||||
cached := that.Db.Get("cached", "*", Map{"key": key})
|
||||
|
||||
if cached == nil {
|
||||
return nil
|
||||
@@ -103,7 +103,7 @@ func (that *CacheDb) get(key string) interface{} {
|
||||
}
|
||||
|
||||
data := Map{}
|
||||
data.JsonToMap(cached.GetString("cvalue"))
|
||||
data.JsonToMap(cached.GetString("value"))
|
||||
|
||||
return data.Get("data")
|
||||
}
|
||||
@@ -113,9 +113,9 @@ func (that *CacheDb) set(key string, value interface{}, tim int64) {
|
||||
|
||||
bte, _ := json.Marshal(Map{"data": value})
|
||||
|
||||
num := that.Db.Update("cached", Map{"cvalue": string(bte), "time": time.Now().UnixNano(), "endtime": tim}, Map{"ckey": key})
|
||||
num := that.Db.Update("cached", Map{"value": string(bte), "time": time.Now().UnixNano(), "endtime": tim}, Map{"key": key})
|
||||
if num == int64(0) {
|
||||
that.Db.Insert("cached", Map{"cvalue": string(bte), "time": time.Now().UnixNano(), "endtime": tim, "ckey": key})
|
||||
that.Db.Insert("cached", Map{"value": string(bte), "time": time.Now().UnixNano(), "endtime": tim, "key": key})
|
||||
}
|
||||
|
||||
//随机执行删除命令
|
||||
@@ -130,10 +130,10 @@ func (that *CacheDb) delete(key string) {
|
||||
//如果通配删除
|
||||
if del != -1 {
|
||||
key = Substr(key, 0, del)
|
||||
that.Db.Delete("cached", Map{"ckey": key + "%"})
|
||||
that.Db.Delete("cached", Map{"key": key + "%"})
|
||||
|
||||
} else {
|
||||
that.Db.Delete("cached", Map{"ckey": key})
|
||||
that.Db.Delete("cached", Map{"key": key})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+45
-45
@@ -1,7 +1,7 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
. "../common"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -17,47 +17,47 @@ type CacheMemory struct {
|
||||
mutex *sync.RWMutex
|
||||
}
|
||||
|
||||
func (this *CacheMemory) GetError() *Error {
|
||||
func (that *CacheMemory) GetError() *Error {
|
||||
|
||||
return this.Error
|
||||
return that.Error
|
||||
|
||||
}
|
||||
|
||||
func (this *CacheMemory) SetError(err *Error) {
|
||||
this.Error = err
|
||||
func (that *CacheMemory) SetError(err *Error) {
|
||||
that.Error = err
|
||||
}
|
||||
|
||||
//获取Cache键只能为string类型
|
||||
func (this *CacheMemory) get(key string) interface{} {
|
||||
this.Error.SetError(nil)
|
||||
if this.Map == nil {
|
||||
this.Map = Map{}
|
||||
func (that *CacheMemory) get(key string) interface{} {
|
||||
that.Error.SetError(nil)
|
||||
if that.Map == nil {
|
||||
that.Map = Map{}
|
||||
}
|
||||
|
||||
if this.Map[key] == nil {
|
||||
if that.Map[key] == nil {
|
||||
return nil
|
||||
}
|
||||
data := this.Map.Get(key, this.Error).(cacheData)
|
||||
if this.Error.GetError() != nil {
|
||||
data := that.Map.Get(key, that.Error).(cacheData)
|
||||
if that.Error.GetError() != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if data.time < time.Now().Unix() {
|
||||
delete(this.Map, key)
|
||||
delete(that.Map, key)
|
||||
return nil
|
||||
}
|
||||
return data.data
|
||||
}
|
||||
|
||||
func (this *CacheMemory) refreshMap() {
|
||||
func (that *CacheMemory) refreshMap() {
|
||||
|
||||
go func() {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
for key, v := range this.Map {
|
||||
that.mutex.Lock()
|
||||
defer that.mutex.Unlock()
|
||||
for key, v := range that.Map {
|
||||
data := v.(cacheData)
|
||||
if data.time <= time.Now().Unix() {
|
||||
delete(this.Map, key)
|
||||
delete(that.Map, key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,15 +66,15 @@ func (this *CacheMemory) refreshMap() {
|
||||
}
|
||||
|
||||
//key value ,时间为时间戳
|
||||
func (this *CacheMemory) set(key string, value interface{}, time int64) {
|
||||
this.Error.SetError(nil)
|
||||
func (that *CacheMemory) set(key string, value interface{}, time int64) {
|
||||
that.Error.SetError(nil)
|
||||
var data cacheData
|
||||
|
||||
if this.Map == nil {
|
||||
this.Map = Map{}
|
||||
if that.Map == nil {
|
||||
that.Map = Map{}
|
||||
}
|
||||
|
||||
dd := this.Map[key]
|
||||
dd := that.Map[key]
|
||||
|
||||
if dd == nil {
|
||||
data = cacheData{}
|
||||
@@ -85,74 +85,74 @@ func (this *CacheMemory) set(key string, value interface{}, time int64) {
|
||||
data.time = time
|
||||
data.data = value
|
||||
|
||||
this.Map.Put(key, data)
|
||||
that.Map.Put(key, data)
|
||||
}
|
||||
|
||||
func (this *CacheMemory) delete(key string) {
|
||||
func (that *CacheMemory) delete(key string) {
|
||||
del := strings.Index(key, "*")
|
||||
//如果通配删除
|
||||
if del != -1 {
|
||||
key = Substr(key, 0, del)
|
||||
for k, _ := range this.Map {
|
||||
for k, _ := range that.Map {
|
||||
if strings.Index(k, key) != -1 {
|
||||
delete(this.Map, k)
|
||||
delete(that.Map, k)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
delete(this.Map, key)
|
||||
delete(that.Map, key)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (this *CacheMemory) Cache(key string, data ...interface{}) *Obj {
|
||||
func (that *CacheMemory) Cache(key string, data ...interface{}) *Obj {
|
||||
|
||||
x := RandX(1, 100000)
|
||||
if x > 99950 {
|
||||
this.refreshMap()
|
||||
that.refreshMap()
|
||||
}
|
||||
if this.mutex == nil {
|
||||
this.mutex = &sync.RWMutex{}
|
||||
if that.mutex == nil {
|
||||
that.mutex = &sync.RWMutex{}
|
||||
}
|
||||
|
||||
reData := &Obj{Data: nil}
|
||||
|
||||
if len(data) == 0 {
|
||||
this.mutex.RLock()
|
||||
reData.Data = this.get(key)
|
||||
this.mutex.RUnlock()
|
||||
that.mutex.RLock()
|
||||
reData.Data = that.get(key)
|
||||
that.mutex.RUnlock()
|
||||
return reData
|
||||
}
|
||||
tim := time.Now().Unix()
|
||||
|
||||
if len(data) == 1 && data[0] == nil {
|
||||
this.mutex.Lock()
|
||||
this.delete(key)
|
||||
this.mutex.Unlock()
|
||||
that.mutex.Lock()
|
||||
that.delete(key)
|
||||
that.mutex.Unlock()
|
||||
return reData
|
||||
}
|
||||
|
||||
if len(data) == 1 {
|
||||
|
||||
tim = tim + this.TimeOut
|
||||
tim = tim + that.TimeOut
|
||||
|
||||
}
|
||||
if len(data) == 2 {
|
||||
this.Error.SetError(nil)
|
||||
tempt := ObjToInt64(data[1], this.Error)
|
||||
that.Error.SetError(nil)
|
||||
tempt := ObjToInt64(data[1], that.Error)
|
||||
|
||||
if tempt > tim {
|
||||
|
||||
tim = tempt
|
||||
} else if this.Error.GetError() == nil {
|
||||
} else if that.Error.GetError() == nil {
|
||||
|
||||
tim = tim + tempt
|
||||
}
|
||||
}
|
||||
this.mutex.Lock()
|
||||
this.set(key, data[0], tim)
|
||||
this.mutex.Unlock()
|
||||
that.mutex.Lock()
|
||||
that.set(key, data[0], tim)
|
||||
that.mutex.Unlock()
|
||||
return reData
|
||||
|
||||
}
|
||||
|
||||
Vendored
+51
-51
@@ -1,7 +1,7 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
. "../common"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -20,65 +20,65 @@ type CacheRedis struct {
|
||||
*Error
|
||||
}
|
||||
|
||||
func (this *CacheRedis) GetError() *Error {
|
||||
func (that *CacheRedis) GetError() *Error {
|
||||
|
||||
return this.Error
|
||||
return that.Error
|
||||
|
||||
}
|
||||
|
||||
func (this *CacheRedis) SetError(err *Error) {
|
||||
this.Error = err
|
||||
func (that *CacheRedis) SetError(err *Error) {
|
||||
that.Error = err
|
||||
}
|
||||
|
||||
//唯一标志
|
||||
func (this *CacheRedis) GetTag() int64 {
|
||||
func (that *CacheRedis) GetTag() int64 {
|
||||
|
||||
if this.tag == int64(0) {
|
||||
this.tag = time.Now().UnixNano()
|
||||
if that.tag == int64(0) {
|
||||
that.tag = time.Now().UnixNano()
|
||||
}
|
||||
return this.tag
|
||||
return that.tag
|
||||
}
|
||||
|
||||
func (this *CacheRedis) reCon() bool {
|
||||
func (that *CacheRedis) reCon() bool {
|
||||
var err error
|
||||
this.conn, err = redis.Dial("tcp", this.Host+":"+ObjToStr(this.Port))
|
||||
that.conn, err = redis.Dial("tcp", that.Host+":"+ObjToStr(that.Port))
|
||||
if err != nil {
|
||||
this.conn = nil
|
||||
this.Error.SetError(err)
|
||||
that.conn = nil
|
||||
that.Error.SetError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
if this.Pwd != "" {
|
||||
_, err = this.conn.Do("AUTH", this.Pwd)
|
||||
if that.Pwd != "" {
|
||||
_, err = that.conn.Do("AUTH", that.Pwd)
|
||||
if err != nil {
|
||||
this.conn = nil
|
||||
this.Error.SetError(err)
|
||||
that.conn = nil
|
||||
that.Error.SetError(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
func (this *CacheRedis) del(key string) {
|
||||
func (that *CacheRedis) del(key string) {
|
||||
del := strings.Index(key, "*")
|
||||
if del != -1 {
|
||||
val, err := redis.Strings(this.conn.Do("KEYS", key))
|
||||
val, err := redis.Strings(that.conn.Do("KEYS", key))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
this.conn.Send("MULTI")
|
||||
that.conn.Send("MULTI")
|
||||
for i, _ := range val {
|
||||
this.conn.Send("DEL", val[i])
|
||||
that.conn.Send("DEL", val[i])
|
||||
}
|
||||
this.conn.Do("EXEC")
|
||||
that.conn.Do("EXEC")
|
||||
} else {
|
||||
_, err := this.conn.Do("DEL", key)
|
||||
_, err := that.conn.Do("DEL", key)
|
||||
if err != nil {
|
||||
this.Error.SetError(err)
|
||||
_, err = this.conn.Do("PING")
|
||||
that.Error.SetError(err)
|
||||
_, err = that.conn.Do("PING")
|
||||
if err != nil {
|
||||
if this.reCon() {
|
||||
_, err = this.conn.Do("DEL", key)
|
||||
if that.reCon() {
|
||||
_, err = that.conn.Do("DEL", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,32 +86,32 @@ func (this *CacheRedis) del(key string) {
|
||||
}
|
||||
|
||||
//key value ,时间为时间戳
|
||||
func (this *CacheRedis) set(key string, value string, time int64) {
|
||||
_, err := this.conn.Do("SET", key, value, "EX", ObjToStr(time))
|
||||
func (that *CacheRedis) set(key string, value string, time int64) {
|
||||
_, err := that.conn.Do("SET", key, value, "EX", ObjToStr(time))
|
||||
if err != nil {
|
||||
|
||||
this.Error.SetError(err)
|
||||
_, err = this.conn.Do("PING")
|
||||
that.Error.SetError(err)
|
||||
_, err = that.conn.Do("PING")
|
||||
if err != nil {
|
||||
if this.reCon() {
|
||||
_, err = this.conn.Do("SET", key, value, "EX", ObjToStr(time))
|
||||
if that.reCon() {
|
||||
_, err = that.conn.Do("SET", key, value, "EX", ObjToStr(time))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *CacheRedis) get(key string) *Obj {
|
||||
func (that *CacheRedis) get(key string) *Obj {
|
||||
reData := &Obj{}
|
||||
var err error
|
||||
reData.Data, err = redis.String(this.conn.Do("GET", key))
|
||||
reData.Data, err = redis.String(that.conn.Do("GET", key))
|
||||
if err != nil {
|
||||
reData.Data = nil
|
||||
if !strings.Contains(err.Error(), "nil returned") {
|
||||
this.Error.SetError(err)
|
||||
_, err = this.conn.Do("PING")
|
||||
that.Error.SetError(err)
|
||||
_, err = that.conn.Do("PING")
|
||||
if err != nil {
|
||||
if this.reCon() {
|
||||
reData.Data, err = redis.String(this.conn.Do("GET", key))
|
||||
if that.reCon() {
|
||||
reData.Data, err = redis.String(that.conn.Do("GET", key))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,10 +121,10 @@ func (this *CacheRedis) get(key string) *Obj {
|
||||
return reData
|
||||
}
|
||||
|
||||
func (this *CacheRedis) Cache(key string, data ...interface{}) *Obj {
|
||||
func (that *CacheRedis) Cache(key string, data ...interface{}) *Obj {
|
||||
reData := &Obj{}
|
||||
if this.conn == nil {
|
||||
re := this.reCon()
|
||||
if that.conn == nil {
|
||||
re := that.reCon()
|
||||
if !re {
|
||||
return reData
|
||||
}
|
||||
@@ -132,38 +132,38 @@ func (this *CacheRedis) Cache(key string, data ...interface{}) *Obj {
|
||||
//查询缓存
|
||||
if len(data) == 0 {
|
||||
|
||||
reData = this.get(key)
|
||||
reData = that.get(key)
|
||||
return reData
|
||||
|
||||
}
|
||||
tim := int64(0)
|
||||
//删除缓存
|
||||
if len(data) == 1 && data[0] == nil {
|
||||
this.del(key)
|
||||
that.del(key)
|
||||
return reData
|
||||
}
|
||||
//添加缓存
|
||||
if len(data) == 1 {
|
||||
|
||||
if this.TimeOut == 0 {
|
||||
//this.Time = Config.GetInt64("cacheShortTime")
|
||||
if that.TimeOut == 0 {
|
||||
//that.Time = Config.GetInt64("cacheShortTime")
|
||||
}
|
||||
|
||||
tim += this.TimeOut
|
||||
tim += that.TimeOut
|
||||
}
|
||||
if len(data) == 2 {
|
||||
this.Error.SetError(nil)
|
||||
tempt := ObjToInt64(data[1], this.Error)
|
||||
that.Error.SetError(nil)
|
||||
tempt := ObjToInt64(data[1], that.Error)
|
||||
if tempt > tim {
|
||||
|
||||
tim = tempt
|
||||
} else if this.GetError() == nil {
|
||||
} else if that.GetError() == nil {
|
||||
|
||||
tim = tim + tempt
|
||||
}
|
||||
}
|
||||
|
||||
this.set(key, ObjToStr(data[0]), tim)
|
||||
that.set(key, ObjToStr(data[0]), tim)
|
||||
|
||||
return reData
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
. "../common"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
)
|
||||
|
||||
type CacheIns interface {
|
||||
|
||||
+141
-50
@@ -1,20 +1,34 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
. "../common"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
)
|
||||
|
||||
var Config = Map{
|
||||
"name": "HoTimeDashBoard",
|
||||
"id": "2f92h3herh23rh2y8",
|
||||
"name": "HoTimeDashBoard",
|
||||
//"id": "2f92h3herh23rh2y8",
|
||||
"label": "HoTime管理平台",
|
||||
|
||||
"stop": Slice{"role", "org"}, //不更新的,同时不允许修改用户自身对应的表数据
|
||||
"labelConfig": Map{
|
||||
"show": "开启",
|
||||
"add": "添加",
|
||||
"delete": "删除",
|
||||
"edit": "编辑",
|
||||
"info": "查看详情",
|
||||
"download": "下载清单",
|
||||
},
|
||||
"menus": []Map{
|
||||
{"label": "平台首页", "name": "HelloWorld", "icon": "el-icon-s-home"},
|
||||
//{"label": "平台首页", "name": "HelloWorld", "icon": "el-icon-s-home"},
|
||||
//{"label": "测试表格", "table": "table", "icon": "el-icon-suitcase"},
|
||||
//{"label": "系统管理", "name": "setting", "icon": "el-icon-setting",
|
||||
// "menus": []Map{
|
||||
// {"label": "用户管理", "table": "user"},
|
||||
// {"label": "用户管理", "table": "user",
|
||||
// "default": {
|
||||
// "path": "info",
|
||||
// "id": "1"
|
||||
// },
|
||||
// "auth": ["show","edit","info","add","delete"],
|
||||
// },
|
||||
// {"label": "组织管理", "table": "organization"},
|
||||
// {"label": "地区管理", "table": "area"},
|
||||
// {"label": "角色管理", "table": "role"},
|
||||
@@ -34,6 +48,7 @@ var ColumnDataType = map[string]string{
|
||||
"float": "number",
|
||||
"double": "number",
|
||||
"decimal": "number",
|
||||
"integer": "number", //sqlite3
|
||||
"char": "text",
|
||||
"text": "text",
|
||||
"blob": "text",
|
||||
@@ -44,54 +59,130 @@ var ColumnDataType = map[string]string{
|
||||
}
|
||||
|
||||
type ColumnShow struct {
|
||||
Name string
|
||||
List bool
|
||||
Edit bool
|
||||
Info bool
|
||||
Must bool
|
||||
Name string //名称
|
||||
|
||||
List bool //列表权限
|
||||
Edit bool //新增和编辑权限
|
||||
Info bool //详情权限
|
||||
Must bool //字段全匹配
|
||||
Type string //空字符串表示
|
||||
Strict bool //name严格匹配必须是这个词才行
|
||||
}
|
||||
|
||||
var ColumnNameType = []ColumnShow{
|
||||
//通用
|
||||
{"idcard", false, true, true, false, "", false},
|
||||
{"id", true, false, true, false, "", true},
|
||||
{"parent_id", true, true, true, false, "", true},
|
||||
//"sn"{true,true,true,""},
|
||||
{"status", true, true, true, false, "select", false},
|
||||
{"state", true, true, true, false, "select", false},
|
||||
{"sex", true, true, true, false, "select", false},
|
||||
{"delete", false, false, false, false, "", false},
|
||||
var RuleConfig = []Map{
|
||||
{"name": "idcard", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
{"name": "id", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": true, "type": ""},
|
||||
{"name": "sn", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": false, "type": ""},
|
||||
{"name": "parent_ids", "add": false, "list": false, "edit": false, "info": false, "must": false, "strict": true, "type": "index"},
|
||||
{"name": "index", "add": false, "list": false, "edit": false, "info": false, "must": false, "strict": true, "type": "index"},
|
||||
|
||||
{"lat", false, true, true, false, "", false},
|
||||
{"lng", false, true, true, false, "", false},
|
||||
{"latitude", false, true, true, false, "", false},
|
||||
{"longitude", false, true, true, false, "", false},
|
||||
{"name": "parent_id", "add": true, "list": true, "edit": true, "info": true, "must": false, "true": false, "type": ""},
|
||||
|
||||
{"index", false, false, false, false, "index", false},
|
||||
{"password", false, true, false, false, "password", false},
|
||||
{"pwd", false, true, false, false, "password", false},
|
||||
{"info", false, true, true, false, "", false},
|
||||
{"version", false, false, false, false, "", false},
|
||||
{"seq", false, true, true, false, "", false},
|
||||
{"sort", false, true, true, false, "", false},
|
||||
{"note", false, true, true, false, "", false},
|
||||
{"description", false, true, true, false, "", false},
|
||||
{"abstract", false, true, true, false, "", false},
|
||||
{"content", false, true, true, false, "", false},
|
||||
{"address", false, true, true, false, "", false},
|
||||
{"full_name", false, true, true, false, "", false},
|
||||
{"create_time", false, false, true, false, "time", true},
|
||||
{"modify_time", true, false, true, false, "time", true},
|
||||
{"image", false, true, true, false, "image", false},
|
||||
{"img", false, true, true, false, "image", false},
|
||||
{"icon", false, true, true, false, "image", false},
|
||||
{"avatar", false, true, true, false, "image", false},
|
||||
{"file", false, true, true, false, "file", false},
|
||||
{"age", false, true, true, false, "", false},
|
||||
{"email", false, true, true, false, "", false},
|
||||
{"time", true, true, true, true, "time", false},
|
||||
{"level", false, false, true, false, "", false},
|
||||
{"rule", true, true, true, false, "form", false},
|
||||
{"name": "amount", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": true, "type": "money"},
|
||||
|
||||
{"name": "info", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "textArea"},
|
||||
|
||||
{"name": "status", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "select"},
|
||||
{"name": "state", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "select"},
|
||||
{"name": "sex", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "select"},
|
||||
|
||||
{"name": "delete", "add": false, "list": false, "edit": false, "info": false, "must": false, "strict": false, "type": ""},
|
||||
|
||||
{"name": "lat", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
{"name": "lng", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
|
||||
{"name": "latitude", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
{"name": "longitude", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
|
||||
{"name": "password", "add": true, "list": false, "edit": true, "info": false, "must": false, "strict": false, "type": "password"},
|
||||
{"name": "pwd", "add": true, "list": false, "edit": true, "info": false, "must": false, "strict": false, "type": "password"},
|
||||
|
||||
{"name": "version", "add": false, "list": false, "edit": false, "info": false, "must": false, "strict": false, "type": ""},
|
||||
{"name": "seq", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
|
||||
{"name": "sort", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
{"name": "note", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
|
||||
{"name": "description", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
|
||||
{"name": "abstract", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
|
||||
{"name": "content", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "textArea"},
|
||||
|
||||
{"name": "address", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
{"name": "full_name", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
|
||||
{"name": "create_time", "add": false, "list": false, "edit": false, "info": true, "must": false, "strict": true, "type": "time"},
|
||||
{"name": "modify_time", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": true, "type": "time"},
|
||||
|
||||
{"name": "image", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "image"},
|
||||
|
||||
{"name": "img", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "image"},
|
||||
{"name": "avatar", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "image"},
|
||||
{"name": "icon", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "image"},
|
||||
|
||||
{"name": "file", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "file"},
|
||||
|
||||
{"name": "age", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
{"name": "email", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||
{"name": "time", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "time"},
|
||||
|
||||
{"name": "level", "add": false, "list": false, "edit": false, "info": true, "must": false, "strict": false, "type": ""},
|
||||
{"name": "rule", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "form"},
|
||||
|
||||
{"name": "auth", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "auth"},
|
||||
|
||||
{"name": "table", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": false, "type": "table"},
|
||||
{"name": "table_id", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": false, "type": "table_id"},
|
||||
}
|
||||
|
||||
//var ColumnNameType = []ColumnShow{
|
||||
// //通用
|
||||
// {"idcard", false, true, true, false, "", false},
|
||||
// {"id", true, false, true, false, "", true},
|
||||
// {"sn", true, false, true, false, "", false},
|
||||
// {"parent_ids", false, false, false, false, "index", true},
|
||||
// {"parent_id", true, true, true, false, "", true},
|
||||
// {"amount", true, true, true, false, "money", true},
|
||||
// {"info", false, true, true, false, "textArea", false},
|
||||
// //"sn"{true,true,true,""},
|
||||
// {"status", true, true, true, false, "select", false},
|
||||
// {"state", true, true, true, false, "select", false},
|
||||
// {"sex", true, true, true, false, "select", false},
|
||||
// {"delete", false, false, false, false, "", false},
|
||||
//
|
||||
// {"lat", false, true, true, false, "", false},
|
||||
// {"lng", false, true, true, false, "", false},
|
||||
// {"latitude", false, true, true, false, "", false},
|
||||
// {"longitude", false, true, true, false, "", false},
|
||||
//
|
||||
// {"index", false, false, false, false, "index", false},
|
||||
//
|
||||
// {"password", false, true, false, false, "password", false},
|
||||
// {"pwd", false, true, false, false, "password", false},
|
||||
//
|
||||
// {"version", false, false, false, false, "", false},
|
||||
// {"seq", false, true, true, false, "", false},
|
||||
// {"sort", false, true, true, false, "", false},
|
||||
// {"note", false, true, true, false, "", false},
|
||||
// {"description", false, true, true, false, "", false},
|
||||
// {"abstract", false, true, true, false, "", false},
|
||||
// {"content", false, true, true, false, "textArea", false},
|
||||
// {"address", true, true, true, false, "", false},
|
||||
// {"full_name", false, true, true, false, "", false},
|
||||
// {"create_time", false, false, true, false, "time", true},
|
||||
// {"modify_time", true, false, true, false, "time", true},
|
||||
// {"image", false, true, true, false, "image", false},
|
||||
// {"img", false, true, true, false, "image", false},
|
||||
// {"icon", false, true, true, false, "image", false},
|
||||
// {"avatar", false, true, true, false, "image", false},
|
||||
// {"file", false, true, true, false, "file", false},
|
||||
// {"age", false, true, true, false, "", false},
|
||||
// {"email", false, true, true, false, "", false},
|
||||
// {"time", true, true, true, false, "time", false},
|
||||
// {"level", false, false, true, false, "", false},
|
||||
// {"rule", true, true, true, false, "form", false},
|
||||
// {"auth", false, true, true, false, "auth", true},
|
||||
// {"table", true, false, true, false, "table", false},
|
||||
// {"table_id", true, false, true, false, "table_id", false},
|
||||
//}
|
||||
|
||||
+757
-349
File diff suppressed because it is too large
Load Diff
+33
-27
@@ -3,8 +3,8 @@ package code
|
||||
var InitTpt = `package {{name}}
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
)
|
||||
|
||||
var ID = "{{id}}"
|
||||
@@ -14,30 +14,32 @@ var Project = Proj{
|
||||
//"user": UserCtr,
|
||||
{{tablesCtr}}
|
||||
"hotime":Ctr{
|
||||
"login": func(this *Context) {
|
||||
name := this.Req.FormValue("name")
|
||||
password := this.Req.FormValue("password")
|
||||
"login": func(that *Context) {
|
||||
|
||||
name := that.Req.FormValue("name")
|
||||
password := that.Req.FormValue("password")
|
||||
if name == "" || password == "" {
|
||||
this.Display(3, "参数不足")
|
||||
that.Display(3, "参数不足")
|
||||
return
|
||||
}
|
||||
user := this.Db.Get("admin", "*", Map{"AND": Map{"OR":Map{"name": name,"phone":name}, "password": Md5(password)}})
|
||||
user := that.Db.Get("admin", "*", Map{"AND": Map{"OR":Map{"name": name,"phone":name}, "password": Md5(password)}})
|
||||
if user == nil {
|
||||
this.Display(5, "登录失败")
|
||||
that.Display(5, "登录失败")
|
||||
return
|
||||
}
|
||||
this.Session("admin_id", user.GetCeilInt("id"))
|
||||
this.Session("admin_name", name)
|
||||
this.Display(0, this.SessionId)
|
||||
that.Session("admin_id", user.GetCeilInt("id"))
|
||||
that.Session("admin_name", name)
|
||||
that.Display(0, that.SessionId)
|
||||
},
|
||||
"logout": func(this *Context) {
|
||||
this.Session("admin_id", nil)
|
||||
this.Session("admin_name", nil)
|
||||
this.Display(0, "退出登录成功")
|
||||
"logout": func(that *Context) {
|
||||
that.Session("admin_id", nil)
|
||||
that.Session("admin_name", nil)
|
||||
that.Display(0, "退出登录成功")
|
||||
},
|
||||
"info": func(that *Context) {
|
||||
hotimeName := that.RouterString[0]
|
||||
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||
str, inData := that.MakeCode.Info("admin", data, that.Db)
|
||||
str, inData := that.MakeCodeRouter[hotimeName].Info("admin", data, that.Db)
|
||||
where := Map{"id": that.Session("admin_id").ToCeilInt()}
|
||||
if len(inData) ==1 {
|
||||
inData["id"] =where["id"]
|
||||
@@ -52,7 +54,7 @@ var Project = Proj{
|
||||
return
|
||||
}
|
||||
for k, v := range re {
|
||||
column := that.MakeCode.TableColumns["admin"][k]
|
||||
column := that.MakeCodeRouter[hotimeName].TableColumns["admin"][k]
|
||||
if column == nil {
|
||||
continue
|
||||
}
|
||||
@@ -69,15 +71,16 @@ var Project = Proj{
|
||||
var CtrTpt = `package {{name}}
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var {{table}}Ctr = Ctr{
|
||||
"info": func(that *Context) {
|
||||
hotimeName := that.RouterString[0]
|
||||
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||
str, inData := that.MakeCode.Info(that.RouterString[1], data, that.Db)
|
||||
str, inData := that.MakeCodeRouter[hotimeName].Info(that.RouterString[1], data, that.Db)
|
||||
where := Map{"id": that.RouterString[2]}
|
||||
|
||||
if len(inData) ==1 {
|
||||
@@ -96,7 +99,7 @@ var {{table}}Ctr = Ctr{
|
||||
}
|
||||
|
||||
for k, v := range re {
|
||||
column := that.MakeCode.TableColumns[that.RouterString[1]][k]
|
||||
column := that.MakeCodeRouter[hotimeName].TableColumns[that.RouterString[1]][k]
|
||||
if column == nil {
|
||||
continue
|
||||
}
|
||||
@@ -108,7 +111,8 @@ var {{table}}Ctr = Ctr{
|
||||
that.Display(0, re)
|
||||
},
|
||||
"add": func(that *Context) {
|
||||
inData := that.MakeCode.Add(that.RouterString[1], that.Req)
|
||||
hotimeName := that.RouterString[0]
|
||||
inData := that.MakeCodeRouter[hotimeName].Add(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "请求参数不足")
|
||||
return
|
||||
@@ -134,7 +138,8 @@ var {{table}}Ctr = Ctr{
|
||||
that.Display(0, re)
|
||||
},
|
||||
"update": func(that *Context) {
|
||||
inData := that.MakeCode.Edit(that.RouterString[1], that.Req)
|
||||
hotimeName := that.RouterString[0]
|
||||
inData := that.MakeCodeRouter[hotimeName].Edit(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "没有找到要更新的数据")
|
||||
return
|
||||
@@ -165,7 +170,8 @@ var {{table}}Ctr = Ctr{
|
||||
that.Display(0, re)
|
||||
},
|
||||
"remove": func(that *Context) {
|
||||
inData := that.MakeCode.Delete(that.RouterString[1], that.Req)
|
||||
hotimeName := that.RouterString[0]
|
||||
inData := that.MakeCodeRouter[hotimeName].Delete(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "请求参数不足")
|
||||
return
|
||||
@@ -186,10 +192,10 @@ var {{table}}Ctr = Ctr{
|
||||
},
|
||||
|
||||
"search": func(that *Context) {
|
||||
|
||||
hotimeName := that.RouterString[0]
|
||||
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||
|
||||
columnStr, leftJoin, where := that.MakeCode.Search(that.RouterString[1], data, that.Req, that.Db)
|
||||
columnStr, leftJoin, where := that.MakeCodeRouter[hotimeName].Search(that.RouterString[1], data, that.Req, that.Db)
|
||||
|
||||
page := ObjToInt(that.Req.FormValue("page"))
|
||||
pageSize := ObjToInt(that.Req.FormValue("pageSize"))
|
||||
@@ -208,7 +214,7 @@ var {{table}}Ctr = Ctr{
|
||||
|
||||
for _, v := range reData {
|
||||
for k, _ := range v {
|
||||
column := that.MakeCode.TableColumns[that.RouterString[1]][k]
|
||||
column := that.MakeCodeRouter[hotimeName].TableColumns[that.RouterString[1]][k]
|
||||
if column == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ type ContextBase struct {
|
||||
}
|
||||
|
||||
//唯一标志
|
||||
func (this *ContextBase) GetTag() string {
|
||||
func (that *ContextBase) GetTag() string {
|
||||
|
||||
if this.tag == "" {
|
||||
this.tag = ObjToStr(time.Now().Unix()) + ":" + ObjToStr(Random())
|
||||
if that.tag == "" {
|
||||
that.tag = ObjToStr(time.Now().Unix()) + ":" + ObjToStr(Random())
|
||||
}
|
||||
return this.tag
|
||||
return that.tag
|
||||
}
|
||||
|
||||
+42
-5
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/hex"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
//安全锁
|
||||
@@ -36,7 +37,43 @@ func StrFirstToUpper(str string) string {
|
||||
return strings.ToUpper(first) + other
|
||||
}
|
||||
|
||||
//相似度计算 ld compares two strings and returns the levenshtein distance between them.
|
||||
// 时间转字符串,第二个参数支持1-5对应显示年月日时分秒
|
||||
func Time2Str(t time.Time, qu ...interface{}) string {
|
||||
if t.Unix() < 0 {
|
||||
return ""
|
||||
}
|
||||
tp := 5
|
||||
if len(qu) != 0 {
|
||||
tp = (qu[0]).(int)
|
||||
}
|
||||
|
||||
switch tp {
|
||||
case 1:
|
||||
return t.Format("2006-01")
|
||||
case 2:
|
||||
return t.Format("2006-01-02")
|
||||
case 3:
|
||||
return t.Format("2006-01-02 15")
|
||||
case 4:
|
||||
return t.Format("2006-01-02 15:04")
|
||||
case 5:
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
case 12:
|
||||
return t.Format("01-02")
|
||||
case 14:
|
||||
return t.Format("01-02 15:04")
|
||||
case 15:
|
||||
return t.Format("01-02 15:04:05")
|
||||
case 34:
|
||||
return t.Format("15:04")
|
||||
case 35:
|
||||
return t.Format("15:04:05")
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
|
||||
}
|
||||
|
||||
// StrLd 相似度计算 ld compares two strings and returns the levenshtein distance between them.
|
||||
func StrLd(s, t string, ignoreCase bool) int {
|
||||
if ignoreCase {
|
||||
s = strings.ToLower(s)
|
||||
@@ -142,7 +179,7 @@ func Md5(req string) string {
|
||||
return hex.EncodeToString(cipherStr)
|
||||
}
|
||||
|
||||
//随机数
|
||||
// Rand 随机数
|
||||
func Rand(count int) int {
|
||||
res := Random()
|
||||
for i := 0; i < count; i++ {
|
||||
@@ -167,7 +204,7 @@ func Random() float64 {
|
||||
|
||||
}
|
||||
|
||||
//随机数范围
|
||||
// RandX 随机数范围
|
||||
func RandX(small int, max int) int {
|
||||
res := 0
|
||||
//随机对象
|
||||
@@ -219,7 +256,7 @@ func RandX(small int, max int) int {
|
||||
// GetDb()
|
||||
//}
|
||||
|
||||
//复制返回数组
|
||||
// DeepCopyMap 复制返回数组
|
||||
func DeepCopyMap(value interface{}) interface{} {
|
||||
if valueMap, ok := value.(Map); ok {
|
||||
newMap := make(Map)
|
||||
@@ -278,7 +315,7 @@ func DeepCopyMap(value interface{}) interface{} {
|
||||
// }
|
||||
//}
|
||||
|
||||
//浮点数四舍五入保留小数
|
||||
// Round 浮点数四舍五入保留小数
|
||||
func Round(f float64, n int) float64 {
|
||||
pow10_n := math.Pow10(n)
|
||||
return math.Trunc((f+0.5/pow10_n)*pow10_n) / pow10_n
|
||||
|
||||
+66
-40
@@ -4,114 +4,140 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
//hotime的常用map
|
||||
type Map map[string]interface{}
|
||||
|
||||
//获取string
|
||||
func (this Map) GetString(key string, err ...*Error) string {
|
||||
func (that Map) GetString(key string, err ...*Error) string {
|
||||
|
||||
if len(err) != 0 {
|
||||
err[0].SetError(nil)
|
||||
}
|
||||
return ObjToStr((this)[key])
|
||||
return ObjToStr((that)[key])
|
||||
|
||||
}
|
||||
|
||||
func (this *Map) Pointer() *Map {
|
||||
func (that *Map) Pointer() *Map {
|
||||
|
||||
return this
|
||||
return that
|
||||
}
|
||||
|
||||
//增加接口
|
||||
func (this Map) Put(key string, value interface{}) {
|
||||
//if this==nil{
|
||||
// this=Map{}
|
||||
func (that Map) Put(key string, value interface{}) {
|
||||
//if that==nil{
|
||||
// that=Map{}
|
||||
//}
|
||||
this[key] = value
|
||||
that[key] = value
|
||||
}
|
||||
|
||||
//删除接口
|
||||
func (this Map) Delete(key string) {
|
||||
delete(this, key)
|
||||
func (that Map) Delete(key string) {
|
||||
delete(that, key)
|
||||
|
||||
}
|
||||
|
||||
//获取Int
|
||||
func (this Map) GetInt(key string, err ...*Error) int {
|
||||
v := ObjToInt((this)[key], err...)
|
||||
func (that Map) GetInt(key string, err ...*Error) int {
|
||||
v := ObjToInt((that)[key], err...)
|
||||
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
//获取Int
|
||||
func (this Map) GetInt64(key string, err ...*Error) int64 {
|
||||
v := ObjToInt64((this)[key], err...)
|
||||
func (that Map) GetInt64(key string, err ...*Error) int64 {
|
||||
v := ObjToInt64((that)[key], err...)
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
//获取向上取整Int64
|
||||
func (this Map) GetCeilInt64(key string, err ...*Error) int64 {
|
||||
v := ObjToCeilInt64((this)[key], err...)
|
||||
func (that Map) GetCeilInt64(key string, err ...*Error) int64 {
|
||||
v := ObjToCeilInt64((that)[key], err...)
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
//获取向上取整Int
|
||||
func (this Map) GetCeilInt(key string, err ...*Error) int {
|
||||
v := ObjToCeilInt((this)[key], err...)
|
||||
func (that Map) GetCeilInt(key string, err ...*Error) int {
|
||||
v := ObjToCeilInt((that)[key], err...)
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
//获取向上取整float64
|
||||
func (this Map) GetCeilFloat64(key string, err ...*Error) float64 {
|
||||
v := ObjToCeilFloat64((this)[key], err...)
|
||||
func (that Map) GetCeilFloat64(key string, err ...*Error) float64 {
|
||||
v := ObjToCeilFloat64((that)[key], err...)
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
//获取Float64
|
||||
func (this Map) GetFloat64(key string, err ...*Error) float64 {
|
||||
func (that Map) GetFloat64(key string, err ...*Error) float64 {
|
||||
|
||||
v := ObjToFloat64((this)[key], err...)
|
||||
v := ObjToFloat64((that)[key], err...)
|
||||
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
func (this Map) GetSlice(key string, err ...*Error) Slice {
|
||||
func (that Map) GetSlice(key string, err ...*Error) Slice {
|
||||
|
||||
//var v Slice
|
||||
v := ObjToSlice((this)[key], err...)
|
||||
v := ObjToSlice((that)[key], err...)
|
||||
|
||||
return v
|
||||
|
||||
}
|
||||
func (this Map) GetBool(key string, err ...*Error) bool {
|
||||
func (that Map) GetBool(key string, err ...*Error) bool {
|
||||
|
||||
//var v Slice
|
||||
v := ObjToBool((this)[key], err...)
|
||||
v := ObjToBool((that)[key], err...)
|
||||
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
func (this Map) GetMap(key string, err ...*Error) Map {
|
||||
func (that Map) GetTime(key string, err ...*Error) time.Time {
|
||||
|
||||
v := ObjToTime((that)[key], err...)
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
func (that Map) RangeSort(callback func(k string, v interface{}) (isEnd bool)) {
|
||||
testQu := []string{}
|
||||
//testQuData:= qu[0].(Map)
|
||||
for key, _ := range that {
|
||||
//fmt.Println(key, ":", value)
|
||||
testQu = append(testQu, key)
|
||||
}
|
||||
sort.Strings(testQu)
|
||||
for _, k := range testQu {
|
||||
re := callback(k, that[k])
|
||||
if re {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (that Map) GetMap(key string, err ...*Error) Map {
|
||||
//var data Slice
|
||||
|
||||
v := ObjToMap((this)[key], err...)
|
||||
v := ObjToMap((that)[key], err...)
|
||||
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
func (this Map) Get(key string, err ...*Error) interface{} {
|
||||
func (that Map) Get(key string, err ...*Error) interface{} {
|
||||
|
||||
if v, ok := (this)[key]; ok {
|
||||
if v, ok := (that)[key]; ok {
|
||||
return v
|
||||
}
|
||||
e := errors.New("没有存储key及对应的数据")
|
||||
@@ -124,10 +150,10 @@ func (this Map) Get(key string, err ...*Error) interface{} {
|
||||
}
|
||||
|
||||
//请传递指针过来
|
||||
func (this Map) ToStruct(stct interface{}) {
|
||||
func (that Map) ToStruct(stct interface{}) {
|
||||
|
||||
data := reflect.ValueOf(stct).Elem()
|
||||
for k, v := range this {
|
||||
for k, v := range that {
|
||||
ks := StrFirstToUpper(k)
|
||||
dkey := data.FieldByName(ks)
|
||||
if !dkey.IsValid() {
|
||||
@@ -135,13 +161,13 @@ func (this Map) ToStruct(stct interface{}) {
|
||||
}
|
||||
switch dkey.Type().String() {
|
||||
case "int":
|
||||
dkey.SetInt(this.GetInt64(k))
|
||||
dkey.SetInt(that.GetInt64(k))
|
||||
case "int64":
|
||||
dkey.Set(reflect.ValueOf(this.GetInt64(k)))
|
||||
dkey.Set(reflect.ValueOf(that.GetInt64(k)))
|
||||
case "float64":
|
||||
dkey.Set(reflect.ValueOf(this.GetFloat64(k)))
|
||||
dkey.Set(reflect.ValueOf(that.GetFloat64(k)))
|
||||
case "string":
|
||||
dkey.Set(reflect.ValueOf(this.GetString(k)))
|
||||
dkey.Set(reflect.ValueOf(that.GetString(k)))
|
||||
case "interface{}":
|
||||
dkey.Set(reflect.ValueOf(v))
|
||||
}
|
||||
@@ -149,13 +175,13 @@ func (this Map) ToStruct(stct interface{}) {
|
||||
|
||||
}
|
||||
|
||||
func (this Map) ToJsonString() string {
|
||||
return ObjToStr(this)
|
||||
func (that Map) ToJsonString() string {
|
||||
return ObjToStr(that)
|
||||
|
||||
}
|
||||
|
||||
func (this Map) JsonToMap(jsonStr string, err ...*Error) {
|
||||
e := json.Unmarshal([]byte(jsonStr), &this)
|
||||
func (that Map) JsonToMap(jsonStr string, err ...*Error) {
|
||||
e := json.Unmarshal([]byte(jsonStr), &that)
|
||||
if e != nil && len(err) != 0 {
|
||||
err[0].SetError(e)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package common
|
||||
|
||||
import "time"
|
||||
|
||||
//对象封装方便取用
|
||||
type Obj struct {
|
||||
Data interface{}
|
||||
@@ -18,6 +20,13 @@ func (that *Obj) ToInt(err ...Error) int {
|
||||
return ObjToInt(that.Data, &that.Error)
|
||||
}
|
||||
|
||||
func (that *Obj) ToTime(err ...Error) time.Time {
|
||||
if len(err) != 0 {
|
||||
that.Error = err[0]
|
||||
}
|
||||
return ObjToTime(that.Data, &that.Error)
|
||||
}
|
||||
|
||||
func (that *Obj) ToInt64(err ...Error) int64 {
|
||||
if len(err) != 0 {
|
||||
that.Error = err[0]
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
//仅限于hotime.Slice
|
||||
@@ -96,6 +97,68 @@ func ObjToSlice(obj interface{}, e ...*Error) Slice {
|
||||
return v
|
||||
}
|
||||
|
||||
func ObjToTime(obj interface{}, e ...*Error) time.Time {
|
||||
|
||||
tInt := ObjToInt64(obj)
|
||||
//字符串类型,只支持标准mysql datetime格式
|
||||
if tInt == 0 {
|
||||
tStr := ObjToStr(obj)
|
||||
|
||||
if len(tStr) > 18 {
|
||||
t, e := time.Parse("2006-01-02 15:04:05", tStr)
|
||||
if e == nil {
|
||||
return t
|
||||
}
|
||||
} else if len(tStr) > 15 {
|
||||
t, e := time.Parse("2006-01-02 15:04", tStr)
|
||||
if e == nil {
|
||||
return t
|
||||
}
|
||||
} else if len(tStr) > 12 {
|
||||
t, e := time.Parse("2006-01-02 15", tStr)
|
||||
if e == nil {
|
||||
return t
|
||||
}
|
||||
} else if len(tStr) > 9 {
|
||||
t, e := time.Parse("2006-01-02", tStr)
|
||||
if e == nil {
|
||||
return t
|
||||
}
|
||||
} else if len(tStr) > 6 {
|
||||
t, e := time.Parse("2006-01", tStr)
|
||||
if e == nil {
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//纳秒级别
|
||||
if len(ObjToStr(tInt)) > 16 {
|
||||
t := time.Time{}.Add(time.Nanosecond * time.Duration(tInt))
|
||||
return t
|
||||
//微秒级别
|
||||
} else if len(ObjToStr(tInt)) > 13 {
|
||||
t := time.Time{}.Add(time.Microsecond * time.Duration(tInt))
|
||||
return t
|
||||
//毫秒级别
|
||||
} else if len(ObjToStr(tInt)) > 10 {
|
||||
t := time.Time{}.Add(time.Millisecond * time.Duration(tInt))
|
||||
return t
|
||||
//秒级别
|
||||
} else if len(ObjToStr(tInt)) > 9 {
|
||||
t := time.Time{}.Add(time.Second * time.Duration(tInt))
|
||||
return t
|
||||
} else if len(ObjToStr(tInt)) > 3 {
|
||||
t, e := time.Parse("2006", ObjToStr(tInt))
|
||||
if e == nil {
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func ObjToFloat64(obj interface{}, e ...*Error) float64 {
|
||||
var err error
|
||||
v := float64(0)
|
||||
@@ -135,6 +198,15 @@ func ObjToFloat64(obj interface{}, e ...*Error) float64 {
|
||||
err = errors.New("没有合适的转换对象!")
|
||||
}
|
||||
}
|
||||
|
||||
if math.IsNaN(v) {
|
||||
err = errors.New("float64 is NaN")
|
||||
v = 0
|
||||
}
|
||||
if math.IsInf(v, 0) {
|
||||
err = errors.New("float64 is Inf")
|
||||
v = 0
|
||||
}
|
||||
if len(e) != 0 {
|
||||
e[0].SetError(err)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Slice []interface{}
|
||||
@@ -14,6 +15,13 @@ func (that Slice) GetString(key int, err ...*Error) string {
|
||||
return ObjToStr((that)[key])
|
||||
}
|
||||
|
||||
func (that Slice) GetTime(key int, err ...*Error) time.Time {
|
||||
|
||||
v := ObjToTime((that)[key], err...)
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
// GetInt 获取Int
|
||||
func (that Slice) GetInt(key int, err ...*Error) int {
|
||||
v := ObjToInt((that)[key], err...)
|
||||
|
||||
+37
-5
@@ -1,22 +1,25 @@
|
||||
package hotime
|
||||
|
||||
import (
|
||||
. "./cache"
|
||||
. "./common"
|
||||
. "./db"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
. "code.hoteas.com/golang/hotime/db"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
*Application
|
||||
Resp http.ResponseWriter
|
||||
Req *http.Request
|
||||
Log Map //日志有则创建
|
||||
RouterString []string
|
||||
Config Map
|
||||
Db *HoTimeDB
|
||||
RespData Map
|
||||
CacheIns
|
||||
RespFunc func()
|
||||
//CacheIns
|
||||
SessionIns
|
||||
DataSize int
|
||||
HandlerStr string //复写请求url
|
||||
@@ -57,12 +60,41 @@ func (that *Context) Display(statu int, data interface{}) {
|
||||
}
|
||||
|
||||
func (that *Context) View() {
|
||||
|
||||
if that.RespFunc != nil {
|
||||
that.RespFunc()
|
||||
}
|
||||
if that.RespData == nil {
|
||||
return
|
||||
}
|
||||
//创建日志
|
||||
if that.Log != nil {
|
||||
that.Log["time"] = time.Now().Format("2006-01-02 15:04")
|
||||
if that.Session("admin_id").Data != nil {
|
||||
that.Log["admin_id"] = that.Session("admin_id").ToCeilInt()
|
||||
}
|
||||
if that.Session("user_id").Data != nil {
|
||||
that.Log["user_id"] = that.Session("user_id").ToCeilInt()
|
||||
}
|
||||
//负载均衡优化
|
||||
ipStr := ""
|
||||
if that.Req.Header.Get("X-Forwarded-For") != "" {
|
||||
ipStr = that.Req.Header.Get("X-Forwarded-For")
|
||||
} else if that.Req.Header.Get("X-Real-IP") != "" {
|
||||
ipStr = that.Req.Header.Get("X-Real-IP")
|
||||
}
|
||||
//负载均衡优化
|
||||
if ipStr == "" {
|
||||
//RemoteAddr := that.Req.RemoteAddr
|
||||
ipStr = Substr(that.Req.RemoteAddr, 0, strings.Index(that.Req.RemoteAddr, ":"))
|
||||
}
|
||||
that.Log["ip"] = ipStr
|
||||
that.Db.Insert("logs", that.Log)
|
||||
}
|
||||
|
||||
d, err := json.Marshal(that.RespData)
|
||||
if err != nil {
|
||||
that.Display(1, err.Error())
|
||||
that.View()
|
||||
return
|
||||
}
|
||||
that.DataSize = len(d)
|
||||
|
||||
+473
-133
@@ -1,15 +1,17 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"../cache"
|
||||
. "../common"
|
||||
"code.hoteas.com/golang/hotime/cache"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/sirupsen/logrus"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -18,6 +20,7 @@ type HoTimeDB struct {
|
||||
ContextBase
|
||||
DBName string
|
||||
*cache.HoTimeCache
|
||||
Log *logrus.Logger
|
||||
Type string
|
||||
Prefix string
|
||||
LastQuery string
|
||||
@@ -27,6 +30,231 @@ type HoTimeDB struct {
|
||||
limit Slice
|
||||
*sql.Tx //事务对象
|
||||
SlaveDB *sql.DB
|
||||
Mode int //mode为0生产模式,1、为测试模式、2为开发模式
|
||||
}
|
||||
|
||||
type HotimeDBBuilder struct {
|
||||
HoTimeDB *HoTimeDB
|
||||
table string
|
||||
selects []interface{}
|
||||
join Slice
|
||||
where Map
|
||||
lastWhere Map
|
||||
page int
|
||||
pageRow int
|
||||
}
|
||||
|
||||
func (that *HoTimeDB) Table(table string) *HotimeDBBuilder {
|
||||
|
||||
return &HotimeDBBuilder{HoTimeDB: that, table: table, where: Map{}}
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) Get(qu ...interface{}) Map {
|
||||
|
||||
return that.HoTimeDB.Get(that.table, that.join, qu, that.where)
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) Count() int {
|
||||
|
||||
return that.HoTimeDB.Count(that.table, that.join, that.where)
|
||||
}
|
||||
func (that *HotimeDBBuilder) Page(page, pageRow int) *HotimeDBBuilder {
|
||||
that.page = page
|
||||
that.pageRow = pageRow
|
||||
return that
|
||||
}
|
||||
func (that *HotimeDBBuilder) Select(qu ...interface{}) []Map {
|
||||
if that.page != 0 {
|
||||
return that.HoTimeDB.Page(that.page, that.pageRow).PageSelect(that.table, that.join, qu, that.where)
|
||||
}
|
||||
return that.HoTimeDB.Select(that.table, that.join, qu, that.where)
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) Update(qu ...interface{}) int64 {
|
||||
lth := len(qu)
|
||||
if lth == 0 {
|
||||
return 0
|
||||
}
|
||||
data := Map{}
|
||||
if lth == 1 {
|
||||
data = ObjToMap(qu[0])
|
||||
}
|
||||
if lth > 1 {
|
||||
for k := 1; k < lth; k++ {
|
||||
data[ObjToStr(qu[k-1])] = qu[k]
|
||||
k++
|
||||
}
|
||||
}
|
||||
|
||||
return that.HoTimeDB.Update(that.table, data, that.where)
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) Delete() int64 {
|
||||
|
||||
return that.HoTimeDB.Delete(that.table, that.where)
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) LeftJoin(table, joinStr string) *HotimeDBBuilder {
|
||||
that.Join(Map{"[>]" + table: joinStr})
|
||||
return that
|
||||
|
||||
}
|
||||
func (that *HotimeDBBuilder) RightJoin(table, joinStr string) *HotimeDBBuilder {
|
||||
that.Join(Map{"[<]" + table: joinStr})
|
||||
return that
|
||||
|
||||
}
|
||||
func (that *HotimeDBBuilder) InnerJoin(table, joinStr string) *HotimeDBBuilder {
|
||||
that.Join(Map{"[><]" + table: joinStr})
|
||||
return that
|
||||
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) FullJoin(table, joinStr string) *HotimeDBBuilder {
|
||||
that.Join(Map{"[<>]" + table: joinStr})
|
||||
return that
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) Join(qu ...interface{}) *HotimeDBBuilder {
|
||||
lth := len(qu)
|
||||
if lth == 0 {
|
||||
return that
|
||||
}
|
||||
data := Map{}
|
||||
if lth == 1 {
|
||||
data = ObjToMap(qu[0])
|
||||
}
|
||||
if lth > 1 {
|
||||
for k := 1; k < lth; k++ {
|
||||
data[ObjToStr(qu[k-1])] = qu[k]
|
||||
k++
|
||||
}
|
||||
}
|
||||
|
||||
if that.join == nil {
|
||||
that.join = Slice{}
|
||||
}
|
||||
if data == nil {
|
||||
return that
|
||||
}
|
||||
|
||||
that.join = append(that.join, data)
|
||||
return that
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) And(qu ...interface{}) *HotimeDBBuilder {
|
||||
|
||||
lth := len(qu)
|
||||
if lth == 0 {
|
||||
return that
|
||||
}
|
||||
var where Map
|
||||
if lth == 1 {
|
||||
where = ObjToMap(qu[0])
|
||||
}
|
||||
if lth > 1 {
|
||||
where = Map{}
|
||||
for k := 1; k < lth; k++ {
|
||||
where[ObjToStr(qu[k-1])] = qu[k]
|
||||
k++
|
||||
}
|
||||
}
|
||||
|
||||
if where == nil {
|
||||
return that
|
||||
}
|
||||
|
||||
if that.lastWhere != nil {
|
||||
that.lastWhere["AND"] = where
|
||||
that.lastWhere = where
|
||||
return that
|
||||
}
|
||||
that.lastWhere = where
|
||||
that.where = Map{"AND": where}
|
||||
|
||||
return that
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) Or(qu ...interface{}) *HotimeDBBuilder {
|
||||
lth := len(qu)
|
||||
if lth == 0 {
|
||||
return that
|
||||
}
|
||||
var where Map
|
||||
if lth == 1 {
|
||||
where = ObjToMap(qu[0])
|
||||
}
|
||||
if lth > 1 {
|
||||
where = Map{}
|
||||
for k := 1; k < lth; k++ {
|
||||
where[ObjToStr(qu[k-1])] = qu[k]
|
||||
k++
|
||||
}
|
||||
}
|
||||
if where == nil {
|
||||
return that
|
||||
}
|
||||
|
||||
if that.lastWhere != nil {
|
||||
that.lastWhere["OR"] = where
|
||||
that.lastWhere = where
|
||||
return that
|
||||
}
|
||||
that.lastWhere = where
|
||||
that.where = Map{"Or": where}
|
||||
|
||||
return that
|
||||
}
|
||||
func (that *HotimeDBBuilder) Where(qu ...interface{}) *HotimeDBBuilder {
|
||||
|
||||
lth := len(qu)
|
||||
if lth == 0 {
|
||||
return that
|
||||
}
|
||||
var where Map
|
||||
if lth == 1 {
|
||||
where = ObjToMap(qu[0])
|
||||
}
|
||||
if lth > 1 {
|
||||
where = Map{}
|
||||
for k := 1; k < lth; k++ {
|
||||
where[ObjToStr(qu[k-1])] = qu[k]
|
||||
k++
|
||||
}
|
||||
}
|
||||
|
||||
if where == nil {
|
||||
return that
|
||||
}
|
||||
|
||||
if that.lastWhere != nil {
|
||||
that.lastWhere["AND"] = where
|
||||
that.lastWhere = where
|
||||
return that
|
||||
}
|
||||
that.lastWhere = where
|
||||
that.where = Map{"AND": that.lastWhere}
|
||||
|
||||
return that
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) From(table string) *HotimeDBBuilder {
|
||||
that.table = table
|
||||
return that
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) Order(qu ...interface{}) *HotimeDBBuilder {
|
||||
that.where["ORDER"] = ObjToSlice(qu)
|
||||
return that
|
||||
}
|
||||
func (that *HotimeDBBuilder) Limit(qu ...interface{}) *HotimeDBBuilder {
|
||||
that.where["LIMIT"] = ObjToSlice(qu)
|
||||
return that
|
||||
}
|
||||
|
||||
func (that *HotimeDBBuilder) Group(qu ...interface{}) *HotimeDBBuilder {
|
||||
that.where["GROUP"] = ObjToSlice(qu)
|
||||
return that
|
||||
}
|
||||
|
||||
// SetConnect 设置数据库配置连接
|
||||
@@ -42,32 +270,36 @@ func (that *HoTimeDB) GetType() string {
|
||||
}
|
||||
|
||||
// Action 事务,如果action返回true则执行成功;false则回滚
|
||||
func (that *HoTimeDB) Action(action func(db HoTimeDB) bool) bool {
|
||||
db := HoTimeDB{DB: that.DB, HoTimeCache: that.HoTimeCache, Prefix: that.Prefix}
|
||||
func (that *HoTimeDB) Action(action func(db HoTimeDB) (isSuccess bool)) (isSuccess bool) {
|
||||
db := HoTimeDB{that.DB, that.ContextBase, that.DBName,
|
||||
that.HoTimeCache, that.Log, that.Type,
|
||||
that.Prefix, that.LastQuery, that.LastData,
|
||||
that.ConnectFunc, that.LastErr, that.limit, that.Tx,
|
||||
that.SlaveDB, that.Mode}
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
that.LastErr.SetError(err)
|
||||
return false
|
||||
return isSuccess
|
||||
}
|
||||
|
||||
db.Tx = tx
|
||||
|
||||
result := action(db)
|
||||
isSuccess = action(db)
|
||||
|
||||
if !result {
|
||||
if !isSuccess {
|
||||
err = db.Tx.Rollback()
|
||||
if err != nil {
|
||||
that.LastErr.SetError(err)
|
||||
return false
|
||||
return isSuccess
|
||||
}
|
||||
return result
|
||||
return isSuccess
|
||||
}
|
||||
err = db.Tx.Commit()
|
||||
if err != nil {
|
||||
that.LastErr.SetError(err)
|
||||
return false
|
||||
}
|
||||
return result
|
||||
return true
|
||||
}
|
||||
|
||||
func (that *HoTimeDB) InitDb(err ...*Error) *Error {
|
||||
@@ -86,7 +318,7 @@ func (that *HoTimeDB) InitDb(err ...*Error) *Error {
|
||||
e := that.SlaveDB.Ping()
|
||||
that.LastErr.SetError(e)
|
||||
}
|
||||
|
||||
//that.DB.SetConnMaxLifetime(time.Second)
|
||||
return that.LastErr
|
||||
}
|
||||
|
||||
@@ -165,7 +397,7 @@ func (that *HoTimeDB) Row(resl *sql.Rows) []Map {
|
||||
|
||||
//
|
||||
////code=0,1,2 0 backup all,1 backup data,2 backup ddl
|
||||
//func (this *HoTimeDB) Backup(path string, code int) {
|
||||
//func (that *HoTimeDB) Backup(path string, code int) {
|
||||
// var cmd *exec.Cmd
|
||||
// switch code {
|
||||
// case 0:cmd= exec.Command("mysqldump","-h"+ObjToStr(Config["dbHost"]), "-P"+ObjToStr(Config["dbPort"]),"-u"+ObjToStr(Config["dbUser"]), "-p"+ObjToStr(Config["dbPwd"]),ObjToStr(Config["dbName"]))
|
||||
@@ -197,7 +429,7 @@ func (that *HoTimeDB) Row(resl *sql.Rows) []Map {
|
||||
// //db := ``
|
||||
// //fmt.Println(db)
|
||||
// //
|
||||
// //tables := this.Query("show tables")
|
||||
// //tables := that.Query("show tables")
|
||||
// //lth := len(tables)
|
||||
// //if lth == 0 {
|
||||
// // return
|
||||
@@ -212,7 +444,7 @@ func (that *HoTimeDB) Row(resl *sql.Rows) []Map {
|
||||
// //
|
||||
// //for i := 0; i < lth; i++ {
|
||||
// // tt := tables[i]["Tables_in_"+db].(string)
|
||||
// // this.backupSave(path, tt, code)
|
||||
// // that.backupSave(path, tt, code)
|
||||
// // debug.FreeOSMemory()
|
||||
// //}
|
||||
//
|
||||
@@ -259,7 +491,7 @@ func (that *HoTimeDB) backupCol(tt string) string {
|
||||
for k := range data[0] {
|
||||
|
||||
if tempLthData == lthCol-1 {
|
||||
str += "`" + k + "`)"
|
||||
str += "`" + k + "`) "
|
||||
} else {
|
||||
str += "`" + k + "`,"
|
||||
}
|
||||
@@ -283,7 +515,7 @@ func (that *HoTimeDB) backupCol(tt string) string {
|
||||
}
|
||||
|
||||
if m == lthCol-1 {
|
||||
str += v + ")"
|
||||
str += v + ") "
|
||||
|
||||
} else {
|
||||
str += v + ","
|
||||
@@ -305,7 +537,11 @@ func (that *HoTimeDB) md5(query string, args ...interface{}) string {
|
||||
}
|
||||
|
||||
func (that *HoTimeDB) Query(query string, args ...interface{}) []Map {
|
||||
|
||||
defer func() {
|
||||
if that.Mode != 0 {
|
||||
that.Log.Info("SQL:"+that.LastQuery, " DATA:", that.LastData, " ERROR:", that.LastErr.GetError())
|
||||
}
|
||||
}()
|
||||
//fmt.Println(query)
|
||||
var err error
|
||||
var resl *sql.Rows
|
||||
@@ -330,16 +566,18 @@ func (that *HoTimeDB) Query(query string, args ...interface{}) []Map {
|
||||
resl, err = db.Query(query, args...)
|
||||
}
|
||||
|
||||
if err != nil && that.LastErr.GetError() != nil &&
|
||||
that.LastErr.GetError().Error() == err.Error() {
|
||||
return nil
|
||||
}
|
||||
|
||||
that.LastErr.SetError(err)
|
||||
if err != nil {
|
||||
if err = db.Ping(); err != nil {
|
||||
that.LastErr.SetError(err)
|
||||
_ = that.InitDb()
|
||||
if that.LastErr.GetError() != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = db.Ping(); err == nil {
|
||||
return that.Query(query, args...)
|
||||
}
|
||||
that.LastErr.SetError(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -347,7 +585,11 @@ func (that *HoTimeDB) Query(query string, args ...interface{}) []Map {
|
||||
}
|
||||
|
||||
func (that *HoTimeDB) Exec(query string, args ...interface{}) (sql.Result, *Error) {
|
||||
|
||||
defer func() {
|
||||
if that.Mode != 0 {
|
||||
that.Log.Info("SQL: "+that.LastQuery, " DATA: ", that.LastData, " ERROR: ", that.LastErr.GetError())
|
||||
}
|
||||
}()
|
||||
that.LastQuery = query
|
||||
that.LastData = args
|
||||
var e error
|
||||
@@ -365,25 +607,25 @@ func (that *HoTimeDB) Exec(query string, args ...interface{}) (sql.Result, *Erro
|
||||
resl, e = that.DB.Exec(query, args...)
|
||||
}
|
||||
|
||||
if e != nil && that.LastErr.GetError() != nil &&
|
||||
that.LastErr.GetError().Error() == e.Error() {
|
||||
return resl, that.LastErr
|
||||
}
|
||||
that.LastErr.SetError(e)
|
||||
|
||||
//判断是否连接断开了
|
||||
if e != nil {
|
||||
|
||||
if e = that.DB.Ping(); e != nil {
|
||||
that.LastErr.SetError(e)
|
||||
_ = that.InitDb()
|
||||
if that.LastErr.GetError() != nil {
|
||||
return resl, that.LastErr
|
||||
}
|
||||
if e = that.DB.Ping(); e == nil {
|
||||
return that.Exec(query, args...)
|
||||
}
|
||||
that.LastErr.SetError(e)
|
||||
return resl, that.LastErr
|
||||
}
|
||||
|
||||
return resl, that.LastErr
|
||||
}
|
||||
|
||||
//func (this *HoTimeDB)copy(data []Map)[]Map{
|
||||
//func (that *HoTimeDB)copy(data []Map)[]Map{
|
||||
// if data==nil{
|
||||
// return nil
|
||||
// }
|
||||
@@ -420,17 +662,20 @@ func (that *HoTimeDB) Select(table string, qu ...interface{}) []Map {
|
||||
if reflect.ValueOf(qu[intQs]).Type().String() == "string" {
|
||||
query += " " + qu[intQs].(string)
|
||||
} else {
|
||||
for i := 0; i < len(qu[intQs].(Slice)); i++ {
|
||||
k := qu[intQs].(Slice)[i].(string)
|
||||
if strings.Contains(k, " AS ") {
|
||||
data := ObjToSlice(qu[intQs])
|
||||
for i := 0; i < len(data); i++ {
|
||||
k := data.GetString(i)
|
||||
if strings.Contains(k, " AS ") ||
|
||||
strings.Contains(k, ".") {
|
||||
|
||||
query += " " + k + " "
|
||||
|
||||
} else {
|
||||
|
||||
query += " `" + k + "` "
|
||||
}
|
||||
|
||||
if i+1 != len(qu[intQs].(Slice)) {
|
||||
if i+1 != len(data) {
|
||||
query = query + ", "
|
||||
}
|
||||
|
||||
@@ -441,24 +686,75 @@ func (that *HoTimeDB) Select(table string, qu ...interface{}) []Map {
|
||||
query += " *"
|
||||
}
|
||||
if !strings.Contains(table, ".") && !strings.Contains(table, " AS ") {
|
||||
query += " FROM `" + that.Prefix + table + "`"
|
||||
query += " FROM `" + that.Prefix + table + "` "
|
||||
} else {
|
||||
query += " FROM " + that.Prefix + table
|
||||
query += " FROM " + that.Prefix + table + " "
|
||||
}
|
||||
|
||||
if join {
|
||||
for k, v := range qu[0].(Map) {
|
||||
var testQu = []string{}
|
||||
testQuData := Map{}
|
||||
if reflect.ValueOf(qu[0]).Type().String() == "common.Map" {
|
||||
testQuData = qu[0].(Map)
|
||||
for key, _ := range testQuData {
|
||||
//fmt.Println(key, ":", value)
|
||||
testQu = append(testQu, key)
|
||||
}
|
||||
}
|
||||
|
||||
if reflect.ValueOf(qu[0]).Type().String() == "common.Slice" {
|
||||
qu0 := ObjToSlice(qu[0])
|
||||
for key, _ := range qu0 {
|
||||
v := qu0.GetMap(key)
|
||||
for k1, v1 := range v {
|
||||
testQu = append(testQu, k1)
|
||||
testQuData[k1] = v1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(testQu)
|
||||
|
||||
for _, k := range testQu {
|
||||
v := testQuData[k]
|
||||
switch Substr(k, 0, 3) {
|
||||
case "[>]":
|
||||
query += " LEFT JOIN " + Substr(k, 3, len(k)-3) + " ON " + v.(string)
|
||||
func() {
|
||||
table := Substr(k, 3, len(k)-3)
|
||||
if !strings.Contains(table, " ") {
|
||||
table = "`" + table + "`"
|
||||
}
|
||||
query += " LEFT JOIN " + table + " ON " + v.(string) + " "
|
||||
}()
|
||||
case "[<]":
|
||||
query += " RIGHT JOIN " + Substr(k, 3, len(k)-3) + " ON " + v.(string)
|
||||
func() {
|
||||
table := Substr(k, 3, len(k)-3)
|
||||
if !strings.Contains(table, " ") {
|
||||
table = "`" + table + "`"
|
||||
}
|
||||
query += " RIGHT JOIN " + table + " ON " + v.(string) + " "
|
||||
}()
|
||||
|
||||
}
|
||||
switch Substr(k, 0, 4) {
|
||||
case "[<>]":
|
||||
query += " FULL JOIN " + Substr(k, 4, len(k)-4) + " ON " + v.(string)
|
||||
func() {
|
||||
table := Substr(k, 4, len(k)-4)
|
||||
if !strings.Contains(table, " ") {
|
||||
table = "`" + table + "`"
|
||||
}
|
||||
query += " FULL JOIN " + table + " ON " + v.(string) + " "
|
||||
}()
|
||||
|
||||
case "[><]":
|
||||
query += " INNER JOIN " + Substr(k, 4, len(k)-4) + " ON " + v.(string)
|
||||
func() {
|
||||
table := Substr(k, 4, len(k)-4)
|
||||
if !strings.Contains(table, " ") {
|
||||
table = "`" + table + "`"
|
||||
}
|
||||
query += " INNER JOIN " + table + " ON " + v.(string) + " "
|
||||
}()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -469,7 +765,7 @@ func (that *HoTimeDB) Select(table string, qu ...interface{}) []Map {
|
||||
|
||||
temp, resWhere := that.where(where)
|
||||
|
||||
query += temp
|
||||
query += temp + ";"
|
||||
qs = append(qs, resWhere...)
|
||||
md5 := that.md5(query, qs...)
|
||||
|
||||
@@ -560,7 +856,15 @@ func (that *HoTimeDB) where(data Map) (string, []interface{}) {
|
||||
|
||||
res := make([]interface{}, 0)
|
||||
//AND OR判断
|
||||
for k, v := range data {
|
||||
testQu := []string{}
|
||||
//testQuData:= qu[0].(Map)
|
||||
for key, _ := range data {
|
||||
//fmt.Println(key, ":", value)
|
||||
testQu = append(testQu, key)
|
||||
}
|
||||
sort.Strings(testQu)
|
||||
for _, k := range testQu {
|
||||
v := data[k]
|
||||
x := 0
|
||||
for i := 0; i < len(condition); i++ {
|
||||
|
||||
@@ -588,8 +892,7 @@ func (that *HoTimeDB) where(data Map) (string, []interface{}) {
|
||||
if v != nil && reflect.ValueOf(v).Type().String() == "common.Slice" && len(v.(Slice)) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if v != nil && reflect.ValueOf(v).Type().String() == "[]interface {}" && len(v.([]interface{})) == 0 {
|
||||
if v != nil && strings.Contains(reflect.ValueOf(v).Type().String(), "[]") && len(ObjToSlice(v)) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -602,39 +905,56 @@ func (that *HoTimeDB) where(data Map) (string, []interface{}) {
|
||||
}
|
||||
|
||||
if len(where) != 0 {
|
||||
where = " WHERE " + where
|
||||
hasWhere := true
|
||||
for _, v := range vcond {
|
||||
if strings.Index(where, v) == 0 {
|
||||
hasWhere = false
|
||||
}
|
||||
}
|
||||
|
||||
if hasWhere {
|
||||
where = " WHERE " + where + " "
|
||||
}
|
||||
}
|
||||
|
||||
//特殊字符
|
||||
for j := 0; j < len(vcond); j++ {
|
||||
for k, v := range data {
|
||||
testQu := []string{}
|
||||
//testQuData:= qu[0].(Map)
|
||||
for key, _ := range data {
|
||||
//fmt.Println(key, ":", value)
|
||||
testQu = append(testQu, key)
|
||||
}
|
||||
sort.Strings(testQu)
|
||||
for _, k := range testQu {
|
||||
v := data[k]
|
||||
if vcond[j] == k {
|
||||
if k == "ORDER" {
|
||||
where += " " + k + " BY "
|
||||
where += k + " BY "
|
||||
//fmt.Println(reflect.ValueOf(v).Type())
|
||||
|
||||
//break
|
||||
} else if k == "GROUP" {
|
||||
|
||||
where += " " + k + " BY "
|
||||
where += k + " BY "
|
||||
} else {
|
||||
|
||||
where += " " + k
|
||||
where += k
|
||||
}
|
||||
|
||||
if reflect.ValueOf(v).Type().String() == "common.Slice" {
|
||||
for i := 0; i < len(v.(Slice)); i++ {
|
||||
where += " " + ObjToStr(v.(Slice)[i])
|
||||
where += " " + ObjToStr(v.(Slice)[i]) + " "
|
||||
|
||||
if len(v.(Slice)) != i+1 {
|
||||
where += ","
|
||||
where += ", "
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
where += " " + ObjToStr(v)
|
||||
where += " " + ObjToStr(v) + " "
|
||||
}
|
||||
|
||||
break
|
||||
@@ -662,61 +982,64 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) {
|
||||
case "[>]":
|
||||
k = strings.Replace(k, "[>]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + ">? "
|
||||
res = append(res, v)
|
||||
case "[<]":
|
||||
k = strings.Replace(k, "[<]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + "<? "
|
||||
res = append(res, v)
|
||||
case "[!]":
|
||||
k = strings.Replace(k, "[!]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where, res = that.notIn(k, v, where, res)
|
||||
case "[#]":
|
||||
k = strings.Replace(k, "[#]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += " " + k + "=" + ObjToStr(v)
|
||||
where += " " + k + "=" + ObjToStr(v) + " "
|
||||
case "[##]": //直接添加value到sql,需要考虑防注入,value比如:"a>b"
|
||||
|
||||
where += " " + ObjToStr(v)
|
||||
case "[#!]":
|
||||
k = strings.Replace(k, "[#!]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += " " + k + "!=" + ObjToStr(v)
|
||||
where += " " + k + "!=" + ObjToStr(v) + " "
|
||||
case "[!#]":
|
||||
k = strings.Replace(k, "[!#]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += " " + k + "!=" + ObjToStr(v)
|
||||
where += " " + k + "!=" + ObjToStr(v) + " "
|
||||
case "[~]":
|
||||
k = strings.Replace(k, "[~]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + " LIKE ? "
|
||||
v = "%" + ObjToStr(v) + "%"
|
||||
res = append(res, v)
|
||||
case "[!~]": //左边任意
|
||||
k = strings.Replace(k, "[~]", "", -1)
|
||||
k = strings.Replace(k, "[!~]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + " LIKE ? "
|
||||
v = "%" + ObjToStr(v)
|
||||
v = "%" + ObjToStr(v) + ""
|
||||
res = append(res, v)
|
||||
case "[~!]": //右边任意
|
||||
k = strings.Replace(k, "[~]", "", -1)
|
||||
k = strings.Replace(k, "[~!]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + " LIKE ? "
|
||||
v = ObjToStr(v) + "%"
|
||||
@@ -724,7 +1047,7 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) {
|
||||
case "[~~]": //手动任意
|
||||
k = strings.Replace(k, "[~]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + " LIKE ? "
|
||||
//v = ObjToStr(v)
|
||||
@@ -738,21 +1061,21 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) {
|
||||
case "[>=]":
|
||||
k = strings.Replace(k, "[>=]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + ">=? "
|
||||
res = append(res, v)
|
||||
case "[<=]":
|
||||
k = strings.Replace(k, "[<=]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + "<=? "
|
||||
res = append(res, v)
|
||||
case "[><]":
|
||||
k = strings.Replace(k, "[><]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + " NOT BETWEEN ? AND ? "
|
||||
res = append(res, v.(Slice)[0])
|
||||
@@ -760,29 +1083,31 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) {
|
||||
case "[<>]":
|
||||
k = strings.Replace(k, "[<>]", "", -1)
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
where += k + " BETWEEN ? AND ? "
|
||||
res = append(res, v.(Slice)[0])
|
||||
res = append(res, v.(Slice)[1])
|
||||
default:
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
if reflect.ValueOf(v).Type().String() == "common.Slice" {
|
||||
if reflect.ValueOf(v).Type().String() == "common.Slice" || strings.Contains(reflect.ValueOf(v).Type().String(), "[]") {
|
||||
vs := ObjToSlice(v)
|
||||
if len(vs) == 0 {
|
||||
return where, res
|
||||
}
|
||||
|
||||
where += k + " IN ("
|
||||
res = append(res, v.(Slice)...)
|
||||
if len(v.(Slice)) == 0 {
|
||||
where += ") "
|
||||
} else {
|
||||
for i := 0; i < len(v.(Slice)); i++ {
|
||||
if i+1 != len(v.(Slice)) {
|
||||
where += "?,"
|
||||
} else {
|
||||
where += "?) "
|
||||
}
|
||||
//res=append(res,(v.(Slice))[i])
|
||||
res = append(res, vs...)
|
||||
|
||||
for i := 0; i < len(vs); i++ {
|
||||
if i+1 != len(vs) {
|
||||
where += "?,"
|
||||
} else {
|
||||
where += "?) "
|
||||
}
|
||||
//res=append(res,(v.(Slice))[i])
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -796,35 +1121,28 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) {
|
||||
} else {
|
||||
//fmt.Println(reflect.ValueOf(v).Type().String())
|
||||
if !strings.Contains(k, ".") {
|
||||
k = "`" + k + "`"
|
||||
k = "`" + k + "` "
|
||||
}
|
||||
if v == nil {
|
||||
where += k + " IS NULL"
|
||||
} else if reflect.ValueOf(v).Type().String() == "common.Slice" {
|
||||
|
||||
where += k + " IS NULL "
|
||||
} else if reflect.ValueOf(v).Type().String() == "common.Slice" || strings.Contains(reflect.ValueOf(v).Type().String(), "[]") {
|
||||
vs := ObjToSlice(v)
|
||||
//fmt.Println(v)
|
||||
where += k + " IN ("
|
||||
res = append(res, v.(Slice)...)
|
||||
for i := 0; i < len(v.(Slice)); i++ {
|
||||
if i+1 != len(v.(Slice)) {
|
||||
where += "?,"
|
||||
} else {
|
||||
where += "?) "
|
||||
}
|
||||
//res=append(res,(v.(Slice))[i])
|
||||
if len(vs) == 0 {
|
||||
return where, res
|
||||
}
|
||||
} else if reflect.ValueOf(v).Type().String() == "[]interface {}" {
|
||||
where += k + " IN ("
|
||||
res = append(res, vs...)
|
||||
|
||||
where += k + " IN ("
|
||||
res = append(res, v.([]interface{})...)
|
||||
for i := 0; i < len(v.([]interface{})); i++ {
|
||||
if i+1 != len(v.([]interface{})) {
|
||||
for i := 0; i < len(vs); i++ {
|
||||
if i+1 != len(vs) {
|
||||
where += "?,"
|
||||
} else {
|
||||
where += "?) "
|
||||
}
|
||||
//res=append(res,(v.(Slice))[i])
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
where += k + "=? "
|
||||
@@ -836,7 +1154,7 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) {
|
||||
return where, res
|
||||
}
|
||||
|
||||
// this.Db.Update("user",hotime.Map{"ustate":"1"},hotime.Map{"AND":hotime.Map{"OR":hotime.Map{"uid":4,"uname":"dasda"}},"ustate":1})
|
||||
// that.Db.Update("user",hotime.Map{"ustate":"1"},hotime.Map{"AND":hotime.Map{"OR":hotime.Map{"uid":4,"uname":"dasda"}},"ustate":1})
|
||||
func (that *HoTimeDB) notIn(k string, v interface{}, where string, res []interface{}) (string, []interface{}) {
|
||||
//where:=""
|
||||
//fmt.Println(reflect.ValueOf(v).Type().String())
|
||||
@@ -844,28 +1162,23 @@ func (that *HoTimeDB) notIn(k string, v interface{}, where string, res []interfa
|
||||
|
||||
where += k + " IS NOT NULL "
|
||||
|
||||
} else if reflect.ValueOf(v).Type().String() == "common.Slice" {
|
||||
} else if reflect.ValueOf(v).Type().String() == "common.Slice" || strings.Contains(reflect.ValueOf(v).Type().String(), "[]") {
|
||||
vs := ObjToSlice(v)
|
||||
if len(vs) == 0 {
|
||||
return where, res
|
||||
}
|
||||
where += k + " NOT IN ("
|
||||
res = append(res, v.(Slice)...)
|
||||
for i := 0; i < len(v.(Slice)); i++ {
|
||||
if i+1 != len(v.(Slice)) {
|
||||
where += "?,"
|
||||
} else {
|
||||
where += "?) "
|
||||
}
|
||||
//res=append(res,(v.(Slice))[i])
|
||||
}
|
||||
} else if reflect.ValueOf(v).Type().String() == "[]interface {}" {
|
||||
where += k + " NOT IN ("
|
||||
res = append(res, v.([]interface{})...)
|
||||
for i := 0; i < len(v.([]interface{})); i++ {
|
||||
if i+1 != len(v.([]interface{})) {
|
||||
res = append(res, vs...)
|
||||
|
||||
for i := 0; i < len(vs); i++ {
|
||||
if i+1 != len(vs) {
|
||||
where += "?,"
|
||||
} else {
|
||||
where += "?) "
|
||||
}
|
||||
//res=append(res,(v.(Slice))[i])
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
where += k + " !=? "
|
||||
@@ -881,7 +1194,15 @@ func (that *HoTimeDB) cond(tag string, data Map) (string, []interface{}) {
|
||||
res := make([]interface{}, 0)
|
||||
lens := len(data)
|
||||
//fmt.Println(lens)
|
||||
for k, v := range data {
|
||||
testQu := []string{}
|
||||
//testQuData:= qu[0].(Map)
|
||||
for key, _ := range data {
|
||||
//fmt.Println(key, ":", value)
|
||||
testQu = append(testQu, key)
|
||||
}
|
||||
sort.Strings(testQu)
|
||||
for _, k := range testQu {
|
||||
v := data[k]
|
||||
x := 0
|
||||
for i := 0; i < len(condition); i++ {
|
||||
|
||||
@@ -904,7 +1225,10 @@ func (that *HoTimeDB) cond(tag string, data Map) (string, []interface{}) {
|
||||
if x == len(condition) {
|
||||
|
||||
tv, vv := that.varCond(k, v)
|
||||
|
||||
if tv == "" {
|
||||
lens--
|
||||
continue
|
||||
}
|
||||
res = append(res, vv...)
|
||||
if lens--; lens <= 0 {
|
||||
where += tv + ""
|
||||
@@ -935,7 +1259,7 @@ func (that *HoTimeDB) Update(table string, data Map, where Map) int64 {
|
||||
} else {
|
||||
qs = append(qs, v)
|
||||
}
|
||||
query += "`" + k + "`=" + vstr + ""
|
||||
query += "`" + k + "`=" + vstr + " "
|
||||
if tp--; tp != 0 {
|
||||
query += ", "
|
||||
}
|
||||
@@ -944,7 +1268,7 @@ func (that *HoTimeDB) Update(table string, data Map, where Map) int64 {
|
||||
temp, resWhere := that.where(where)
|
||||
//fmt.Println(resWhere)
|
||||
|
||||
query += temp
|
||||
query += temp + ";"
|
||||
qs = append(qs, resWhere...)
|
||||
|
||||
res, err := that.Exec(query, qs...)
|
||||
@@ -969,7 +1293,7 @@ func (that *HoTimeDB) Delete(table string, data map[string]interface{}) int64 {
|
||||
query := "DELETE FROM " + that.Prefix + table + " "
|
||||
|
||||
temp, resWhere := that.where(data)
|
||||
query += temp
|
||||
query += temp + ";"
|
||||
|
||||
res, err := that.Exec(query, resWhere...)
|
||||
rows := int64(0)
|
||||
@@ -999,13 +1323,29 @@ func (that *HoTimeDB) Insert(table string, data map[string]interface{}) int64 {
|
||||
tempLen := 0
|
||||
for k, v := range data {
|
||||
tempLen++
|
||||
values = append(values, v)
|
||||
if tempLen < lens {
|
||||
queryString += "`" + k + "`,"
|
||||
valueString += "?,"
|
||||
|
||||
vstr := "?"
|
||||
if Substr(k, len(k)-3, 3) == "[#]" {
|
||||
k = strings.Replace(k, "[#]", "", -1)
|
||||
|
||||
vstr = ObjToStr(v)
|
||||
if tempLen < lens {
|
||||
queryString += "`" + k + "`,"
|
||||
valueString += vstr + ","
|
||||
} else {
|
||||
queryString += "`" + k + "`) "
|
||||
valueString += vstr + ");"
|
||||
}
|
||||
} else {
|
||||
queryString += "`" + k + "`) "
|
||||
valueString += "?);"
|
||||
|
||||
values = append(values, v)
|
||||
if tempLen < lens {
|
||||
queryString += "`" + k + "`,"
|
||||
valueString += "?,"
|
||||
} else {
|
||||
queryString += "`" + k + "`) "
|
||||
valueString += "?);"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package aliyun
|
||||
|
||||
import (
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type company struct {
|
||||
ApiCode string
|
||||
Url string
|
||||
}
|
||||
|
||||
var Company = company{}
|
||||
|
||||
func (that *company) Init(apiCode string) {
|
||||
//"06c6a07e89dd45c88de040ee1489eef7"
|
||||
that.ApiCode = apiCode
|
||||
that.Url = "http://api.81api.com"
|
||||
}
|
||||
|
||||
// GetCompanyOtherAll 获取企业基础信息
|
||||
func (that *company) GetCompanyOtherAll(name string) Map {
|
||||
|
||||
res := Map{}
|
||||
data, e := that.GetCompanyPatentsInfo(name) //获取专利信息
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
} else {
|
||||
res["PatentsInfo"] = data.GetMap("data")
|
||||
}
|
||||
data, e = that.GetCompanyOtherCopyrightsInfo(name) //获取其他专利
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
} else {
|
||||
res["OtherCopyrightsInfo"] = data.GetMap("data")
|
||||
}
|
||||
data, e = that.GetCompanyTrademarksInfo(name) //获取商标
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
} else {
|
||||
res["TrademarksInfo"] = data.GetMap("data")
|
||||
}
|
||||
data, e = that.GetCompanySoftwareCopyrightsInfo(name) //获取软著
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
} else {
|
||||
res["SoftwareCopyrightsInfo"] = data.GetMap("data")
|
||||
}
|
||||
data, e = that.GetCompanyProfileTags(name) //获取大数据标签
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
} else {
|
||||
res["ProfileTags"] = data.GetSlice("data")
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetCompanyBaseInfo 获取企业基础信息
|
||||
func (that *company) GetCompanyList(name string) (Map, error) {
|
||||
url := "/fuzzyQueryCompanyInfo/"
|
||||
|
||||
body, err := that.basePost(url, name)
|
||||
return ObjToMap(body), err
|
||||
}
|
||||
|
||||
// GetCompanyBaseInfo 获取企业基础信息
|
||||
func (that *company) GetCompanyBaseInfo(name string) (Map, error) {
|
||||
url := "/getCompanyBaseInfo/"
|
||||
|
||||
body, err := that.basePost(url, name)
|
||||
return ObjToMap(body), err
|
||||
}
|
||||
|
||||
// GetCompanyPatentsInfo 获取专利信息
|
||||
func (that *company) GetCompanyPatentsInfo(name string) (Map, error) {
|
||||
url := "/getCompanyPatentsInfo/"
|
||||
|
||||
body, err := that.basePost(url, name)
|
||||
return ObjToMap(body), err
|
||||
}
|
||||
|
||||
// GetCompanyTrademarksInfo 获取商标信息
|
||||
func (that *company) GetCompanyTrademarksInfo(name string) (Map, error) {
|
||||
url := "/getCompanyTrademarksInfo/"
|
||||
|
||||
body, err := that.basePost(url, name)
|
||||
return ObjToMap(body), err
|
||||
}
|
||||
|
||||
// GetCompanySoftwareCopyrightsInfo 获取软著信息
|
||||
func (that *company) GetCompanySoftwareCopyrightsInfo(name string) (Map, error) {
|
||||
url := "/getCompanySoftwareCopyrightsInfo/"
|
||||
|
||||
body, err := that.basePost(url, name)
|
||||
return ObjToMap(body), err
|
||||
}
|
||||
|
||||
// GetCompanyOtherCopyrightsInfo 获取其他著作信息
|
||||
func (that *company) GetCompanyOtherCopyrightsInfo(name string) (Map, error) {
|
||||
url := "/getCompanyOtherCopyrightsInfo/"
|
||||
|
||||
body, err := that.basePost(url, name)
|
||||
return ObjToMap(body), err
|
||||
}
|
||||
|
||||
// GetCompanyProfileTags 获取大数据标签
|
||||
func (that *company) GetCompanyProfileTags(name string) (Map, error) {
|
||||
url := "/getCompanyProfileTags/"
|
||||
body, err := that.basePost(url, name)
|
||||
return ObjToMap(body), err
|
||||
}
|
||||
func (that *company) basePost(url string, name string) (string, error) {
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
reqest, err := http.NewRequest("GET", that.Url+url+name+"/?isRaiseErrorCode=1", nil)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Fatal error ", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
reqest.Header.Add("Authorization", "APPCODE "+that.ApiCode)
|
||||
response, err := client.Do(reqest)
|
||||
defer response.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Fatal error ", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
res := string(body)
|
||||
fmt.Println(res)
|
||||
return res, err
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package baidu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type baiduMap struct {
|
||||
Ak string
|
||||
Url string
|
||||
}
|
||||
|
||||
var BaiDuMap = baiduMap{}
|
||||
|
||||
func (that *baiduMap) Init(Ak string) {
|
||||
//"ak=ZeT902EZvVgIoGVWEFK3osUm"
|
||||
that.Ak = Ak
|
||||
that.Url = "https://api.map.baidu.com/place/v2/suggestion?output=json" + "&ak=" + Ak
|
||||
//query
|
||||
}
|
||||
|
||||
// GetPosition 获取定位列表
|
||||
func (that *baiduMap) GetPosition(name string, region string) (string, error) {
|
||||
|
||||
client := &http.Client{}
|
||||
if region == "" {
|
||||
region = "全国"
|
||||
}
|
||||
reqest, err := http.NewRequest("GET", that.Url+"&query="+url.PathEscape(name)+"®ion="+url.PathEscape(region), nil)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Fatal error ", err.Error())
|
||||
return "", err
|
||||
}
|
||||
response, err := client.Do(reqest)
|
||||
defer response.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Fatal error ", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//fmt.Println(string(body))
|
||||
|
||||
return string(body), err
|
||||
|
||||
}
|
||||
+17
-21
@@ -10,29 +10,31 @@ import (
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type DDY struct {
|
||||
type dingdongyun struct {
|
||||
ApiKey string
|
||||
YzmUrl string
|
||||
TzUrl string
|
||||
}
|
||||
|
||||
func (this *DDY) Init(apikey string) {
|
||||
this.ApiKey = apikey
|
||||
this.YzmUrl = "https://api.dingdongcloud.com/v2/sms/captcha/send.json"
|
||||
this.TzUrl = "https://api.dingdongcloud.com/v2/sms/notice/send.json"
|
||||
var DDY = dingdongyun{}
|
||||
|
||||
func (that *dingdongyun) Init(apikey string) {
|
||||
that.ApiKey = apikey
|
||||
that.YzmUrl = "https://api.dingdongcloud.com/v2/sms/captcha/send.json"
|
||||
that.TzUrl = "https://api.dingdongcloud.com/v2/sms/notice/send.json"
|
||||
}
|
||||
|
||||
//发送短信验证码 code验证码如:123456 返回true表示发送成功flase表示发送失败
|
||||
func (this *DDY) SendYZM(umoblie string, tpt string, data map[string]string) (bool, error) {
|
||||
// SendYZM 发送短信验证码 code验证码如:123456 返回true表示发送成功flase表示发送失败
|
||||
func (that *dingdongyun) SendYZM(umoblie string, tpt string, data map[string]string) (bool, error) {
|
||||
for k, v := range data {
|
||||
tpt = strings.Replace(tpt, "{"+k+"}", v, -1)
|
||||
}
|
||||
|
||||
return this.send(this.YzmUrl, umoblie, tpt)
|
||||
return that.send(that.YzmUrl, umoblie, tpt)
|
||||
}
|
||||
|
||||
//发送通知
|
||||
func (this *DDY) SendTz(umoblie []string, tpt string, data map[string]string) (bool, error) {
|
||||
// SendTz 发送通知
|
||||
func (that *dingdongyun) SendTz(umoblie []string, tpt string, data map[string]string) (bool, error) {
|
||||
for k, v := range data {
|
||||
tpt = strings.Replace(tpt, "{"+k+"}", v, -1)
|
||||
}
|
||||
@@ -44,14 +46,14 @@ func (this *DDY) SendTz(umoblie []string, tpt string, data map[string]string) (b
|
||||
}
|
||||
umobleStr += "," + v
|
||||
}
|
||||
return this.send(this.TzUrl, umobleStr, tpt)
|
||||
return that.send(that.TzUrl, umobleStr, tpt)
|
||||
}
|
||||
|
||||
//发送短信
|
||||
func (this *DDY) send(mUrl string, umoblie string, content string) (bool, error) {
|
||||
func (that *dingdongyun) send(mUrl string, umoblie string, content string) (bool, error) {
|
||||
|
||||
data_send_sms_yzm := url.Values{"apikey": {this.ApiKey}, "mobile": {umoblie}, "content": {content}}
|
||||
res, err := this.httpsPostForm(mUrl, data_send_sms_yzm)
|
||||
data_send_sms_yzm := url.Values{"apikey": {that.ApiKey}, "mobile": {umoblie}, "content": {content}}
|
||||
res, err := that.httpsPostForm(mUrl, data_send_sms_yzm)
|
||||
if err != nil && res == "" {
|
||||
return false, errors.New("连接错误")
|
||||
}
|
||||
@@ -73,7 +75,7 @@ func (this *DDY) send(mUrl string, umoblie string, content string) (bool, error)
|
||||
}
|
||||
|
||||
//调用url发送短信的连接
|
||||
func (this *DDY) httpsPostForm(url string, data url.Values) (string, error) {
|
||||
func (that *dingdongyun) httpsPostForm(url string, data url.Values) (string, error) {
|
||||
resp, err := http.PostForm(url, data)
|
||||
|
||||
if err != nil {
|
||||
@@ -89,9 +91,3 @@ func (this *DDY) httpsPostForm(url string, data url.Values) (string, error) {
|
||||
return string(body), nil
|
||||
|
||||
}
|
||||
|
||||
var DefaultDDY DDY
|
||||
|
||||
func init() {
|
||||
DefaultDDY = DDY{}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package download
|
||||
|
||||
import (
|
||||
. "../../common"
|
||||
"bytes"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
//下载文件
|
||||
// Down 下载文件
|
||||
func Down(url, path, name string, e ...*Error) bool {
|
||||
|
||||
os.MkdirAll(path, os.ModeDir)
|
||||
@@ -18,13 +18,13 @@ func Down(url, path, name string, e ...*Error) bool {
|
||||
}
|
||||
out, err := os.Create(path + name)
|
||||
|
||||
if err != nil && e[0] != nil {
|
||||
if err != nil && len(e) != 0 {
|
||||
e[0].SetError(err)
|
||||
return false
|
||||
}
|
||||
defer out.Close()
|
||||
resp, err := http.Get(url)
|
||||
if err != nil && e[0] != nil {
|
||||
if err != nil && len(e) != 0 {
|
||||
e[0].SetError(err)
|
||||
return false
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func Down(url, path, name string, e ...*Error) bool {
|
||||
|
||||
pix, err := ioutil.ReadAll(resp.Body)
|
||||
_, err = io.Copy(out, bytes.NewReader(pix))
|
||||
if err != nil && e[0] != nil {
|
||||
if err != nil && len(e) != 0 {
|
||||
e[0].SetError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package mongodb
|
||||
|
||||
import (
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"context"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type MongoDb struct {
|
||||
Client *mongo.Client
|
||||
Ctx context.Context
|
||||
DataBase *mongo.Database
|
||||
Connect *mongo.Collection
|
||||
LastErr error
|
||||
}
|
||||
|
||||
func GetMongoDb(database, url string) (*MongoDb, error) {
|
||||
db := MongoDb{}
|
||||
clientOptions := options.Client().ApplyURI(url)
|
||||
|
||||
db.Ctx = context.TODO()
|
||||
// Connect to MongoDb
|
||||
var err error
|
||||
db.Client, err = mongo.Connect(db.Ctx, clientOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Check the connection
|
||||
err = db.Client.Ping(db.Ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("Connected to MongoDb!")
|
||||
//databases, err := db.Client.ListDatabaseNames(db.Ctx, bson.M{})
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//fmt.Println(databases)
|
||||
db.DataBase = db.Client.Database(database)
|
||||
return &db, nil
|
||||
|
||||
}
|
||||
|
||||
func (that *MongoDb) Insert(table string, data interface{}) string {
|
||||
collection := that.DataBase.Collection(table)
|
||||
re, err := collection.InsertOne(that.Ctx, data)
|
||||
if err != nil {
|
||||
that.LastErr = err
|
||||
return ""
|
||||
}
|
||||
return ObjToStr(re.InsertedID)
|
||||
}
|
||||
|
||||
func (that *MongoDb) InsertMany(table string, data ...interface{}) Slice {
|
||||
collection := that.DataBase.Collection(table)
|
||||
re, err := collection.InsertMany(that.Ctx, data)
|
||||
if err != nil {
|
||||
that.LastErr = err
|
||||
return Slice{}
|
||||
}
|
||||
|
||||
return ObjToSlice(re.InsertedIDs)
|
||||
|
||||
}
|
||||
|
||||
func (that *MongoDb) Update(table string, data Map, where Map) int64 {
|
||||
collection := that.DataBase.Collection(table)
|
||||
re, err := collection.UpdateMany(that.Ctx, where, data)
|
||||
|
||||
if err != nil {
|
||||
that.LastErr = err
|
||||
return 0
|
||||
}
|
||||
|
||||
return re.ModifiedCount
|
||||
|
||||
}
|
||||
|
||||
func (that *MongoDb) Delete(table string, where Map) int64 {
|
||||
collection := that.DataBase.Collection(table)
|
||||
re, err := collection.DeleteMany(that.Ctx, where)
|
||||
|
||||
if err != nil {
|
||||
that.LastErr = err
|
||||
return 0
|
||||
}
|
||||
|
||||
return re.DeletedCount
|
||||
|
||||
}
|
||||
|
||||
func (that *MongoDb) Get(table string, where Map) Map {
|
||||
results := []Map{}
|
||||
var cursor *mongo.Cursor
|
||||
var err error
|
||||
collection := that.DataBase.Collection(table)
|
||||
if cursor, err = collection.Find(that.Ctx, where, options.Find().SetSkip(0), options.Find().SetLimit(2)); err != nil {
|
||||
that.LastErr = err
|
||||
return nil
|
||||
}
|
||||
//延迟关闭游标
|
||||
defer func() {
|
||||
if err := cursor.Close(that.Ctx); err != nil {
|
||||
that.LastErr = err
|
||||
|
||||
}
|
||||
}()
|
||||
//这里的结果遍历可以使用另外一种更方便的方式:
|
||||
if err = cursor.All(that.Ctx, &results); err != nil {
|
||||
that.LastErr = err
|
||||
return nil
|
||||
}
|
||||
if len(results) > 0 {
|
||||
|
||||
return results[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (that MongoDb) Select(table string, where Map, page, pageRow int64) []Map {
|
||||
page = (page - 1) * pageRow
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
results := []Map{}
|
||||
var cursor *mongo.Cursor
|
||||
var err error
|
||||
collection := that.DataBase.Collection(table)
|
||||
if cursor, err = collection.Find(that.Ctx, where, options.Find().SetSkip(page), options.Find().SetLimit(pageRow)); err != nil {
|
||||
that.LastErr = err
|
||||
return results
|
||||
}
|
||||
//延迟关闭游标
|
||||
defer func() {
|
||||
if err := cursor.Close(that.Ctx); err != nil {
|
||||
that.LastErr = err
|
||||
|
||||
}
|
||||
}()
|
||||
//这里的结果遍历可以使用另外一种更方便的方式:
|
||||
|
||||
if err = cursor.All(that.Ctx, &results); err != nil {
|
||||
that.LastErr = err
|
||||
return results
|
||||
}
|
||||
return results
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func FileGet(path string) []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
//RSA加密
|
||||
// RSA_Encrypt RSA加密
|
||||
// plainText 要加密的数据
|
||||
// path 公钥匙文件地址
|
||||
func RSA_Encrypt(plainText []byte, buf []byte) []byte {
|
||||
@@ -46,7 +46,7 @@ func RSA_Encrypt(plainText []byte, buf []byte) []byte {
|
||||
return cipherText
|
||||
}
|
||||
|
||||
//RSA解密
|
||||
// RSA_Decrypt RSA解密
|
||||
// cipherText 需要解密的byte数据
|
||||
// path 私钥文件路径
|
||||
func RSA_Decrypt(cipherText []byte, buf []byte) []byte {
|
||||
@@ -97,7 +97,7 @@ func Demo() {
|
||||
fmt.Println(string(decrypt))
|
||||
}
|
||||
|
||||
//生成RSA私钥和公钥,保存到文件中
|
||||
// GenerateRSAKey 生成RSA私钥和公钥,保存到文件中
|
||||
// bits 证书大小
|
||||
func GenerateRSAKey(bits int, path string) {
|
||||
//GenerateKey函数使用随机数据生成器random生成一对具有指定字位数的RSA密钥
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package tencent
|
||||
|
||||
import (
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
gourl "net/url"
|
||||
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type company struct {
|
||||
secretId string
|
||||
secretKey string
|
||||
}
|
||||
|
||||
var Company = company{}
|
||||
|
||||
func (that *company) Init(secretId, secretKey string) {
|
||||
// 云市场分配的密钥Id
|
||||
//secretId := "xxxx"
|
||||
//// 云市场分配的密钥Key
|
||||
//secretKey := "xxxx"
|
||||
that.secretId = secretId
|
||||
that.secretKey = secretKey
|
||||
}
|
||||
|
||||
func (that *company) calcAuthorization(source string) (auth string, datetime string, err error) {
|
||||
|
||||
timeLocation, _ := time.LoadLocation("Etc/GMT")
|
||||
datetime = time.Now().In(timeLocation).Format("Mon, 02 Jan 2006 15:04:05 GMT")
|
||||
signStr := fmt.Sprintf("x-date: %s\nx-source: %s", datetime, source)
|
||||
|
||||
// hmac-sha1
|
||||
mac := hmac.New(sha1.New, []byte(that.secretKey))
|
||||
mac.Write([]byte(signStr))
|
||||
sign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||
|
||||
auth = fmt.Sprintf("hmac id=\"%s\", algorithm=\"hmac-sha1\", headers=\"x-date x-source\", signature=\"%s\"",
|
||||
that.secretId, sign)
|
||||
|
||||
return auth, datetime, nil
|
||||
}
|
||||
|
||||
func (that *company) urlencode(params map[string]string) string {
|
||||
var p = gourl.Values{}
|
||||
for k, v := range params {
|
||||
p.Add(k, v)
|
||||
}
|
||||
return p.Encode()
|
||||
}
|
||||
|
||||
func (that *company) GetCompany(name string) Map {
|
||||
// 云市场分配的密钥Id
|
||||
//secretId := "xxxx"
|
||||
//// 云市场分配的密钥Key
|
||||
//secretKey := "xxxx"
|
||||
source := "market"
|
||||
|
||||
// 签名
|
||||
auth, datetime, _ := that.calcAuthorization(source)
|
||||
|
||||
// 请求方法
|
||||
method := "GET"
|
||||
// 请求头
|
||||
headers := map[string]string{"X-Source": source, "X-Date": datetime, "Authorization": auth}
|
||||
|
||||
// 查询参数
|
||||
queryParams := make(map[string]string)
|
||||
queryParams["keyword"] = name
|
||||
// body参数
|
||||
bodyParams := make(map[string]string)
|
||||
|
||||
// url参数拼接
|
||||
url := "https://service-3jnh3ku8-1256140209.gz.apigw.tencentcs.com/release/business4/geet"
|
||||
if len(queryParams) > 0 {
|
||||
url = fmt.Sprintf("%s?%s", url, that.urlencode(queryParams))
|
||||
}
|
||||
|
||||
bodyMethods := map[string]bool{"POST": true, "PUT": true, "PATCH": true}
|
||||
var body io.Reader = nil
|
||||
if bodyMethods[method] {
|
||||
body = strings.NewReader(that.urlencode(bodyParams))
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
request, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
for k, v := range headers {
|
||||
request.Header.Set(k, v)
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
bodyBytes, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
res := string(bodyBytes)
|
||||
fmt.Println(res)
|
||||
return ObjToMap(res)
|
||||
}
|
||||
+49
-8
@@ -8,16 +8,57 @@ import (
|
||||
ocr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ocr/v20181119"
|
||||
)
|
||||
|
||||
var credential = common.NewCredential(
|
||||
"AKIDOgT8cKCQksnY7yKATaYO7j9ORJzSYohP",
|
||||
"GNXgjdN4czA9ya0FNMApVJzTmsmU0KSN",
|
||||
)
|
||||
type tencent struct {
|
||||
secretId string
|
||||
secretKey string
|
||||
credential *common.Credential
|
||||
}
|
||||
|
||||
func OCR(base64Str string) string {
|
||||
var Tencent = tencent{}
|
||||
|
||||
func (that *tencent) Init(secretId, secretKey string) {
|
||||
// 云市场分配的密钥Id
|
||||
//secretId := "xxxx"
|
||||
//// 云市场分配的密钥Key
|
||||
//secretKey := "xxxx"
|
||||
that.secretId = secretId
|
||||
that.secretKey = secretKey
|
||||
that.credential = common.NewCredential(
|
||||
that.secretId,
|
||||
that.secretKey,
|
||||
)
|
||||
}
|
||||
|
||||
func (that *tencent) OCRCOMPANY(base64Str string) string {
|
||||
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.Endpoint = "ocr.tencentcloudapi.com"
|
||||
client, _ := ocr.NewClient(credential, "ap-guangzhou", cpf)
|
||||
client, _ := ocr.NewClient(that.credential, "ap-guangzhou", cpf)
|
||||
|
||||
request := ocr.NewBizLicenseOCRRequest()
|
||||
|
||||
//request.ImageUrl = common.StringPtr("https://img0.baidu.com/it/u=2041013181,3227632688&fm=26&fmt=auto")
|
||||
request.ImageBase64 = common.StringPtr(base64Str)
|
||||
|
||||
response, err := client.BizLicenseOCR(request)
|
||||
if _, ok := err.(*errors.TencentCloudSDKError); ok {
|
||||
fmt.Println("An API error has returned: %s", err)
|
||||
return ""
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println("An API error has returned: %s", err)
|
||||
return ""
|
||||
}
|
||||
//fmt.Printf("%s", response.ToJsonString())
|
||||
|
||||
return response.ToJsonString()
|
||||
}
|
||||
|
||||
func (that *tencent) OCR(base64Str string) string {
|
||||
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.Endpoint = "ocr.tencentcloudapi.com"
|
||||
client, _ := ocr.NewClient(that.credential, "ap-guangzhou", cpf)
|
||||
|
||||
request := ocr.NewGeneralAccurateOCRRequest()
|
||||
|
||||
@@ -38,11 +79,11 @@ func OCR(base64Str string) string {
|
||||
return response.ToJsonString()
|
||||
}
|
||||
|
||||
func Qrcode(base64Str string) string {
|
||||
func (that *tencent) Qrcode(base64Str string) string {
|
||||
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.Endpoint = "ocr.tencentcloudapi.com"
|
||||
client, _ := ocr.NewClient(credential, "ap-guangzhou", cpf)
|
||||
client, _ := ocr.NewClient(that.credential, "ap-guangzhou", cpf)
|
||||
|
||||
request := ocr.NewQrcodeOCRRequest()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
. "../../common"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
"errors"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
@@ -15,7 +15,7 @@ type Upload struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func (this *Upload) UpFile(Request *http.Request, fieldName, savefilepath, savePath string) (string, error) {
|
||||
func (that *Upload) UpFile(Request *http.Request, fieldName, savefilepath, savePath string) (string, error) {
|
||||
Request.ParseMultipartForm(32 << 20)
|
||||
var filePath string
|
||||
files := Request.MultipartForm.File
|
||||
@@ -36,7 +36,7 @@ func (this *Upload) UpFile(Request *http.Request, fieldName, savefilepath, saveP
|
||||
data := time.Unix(int64(t), 0).Format("2006-01")
|
||||
path := ""
|
||||
if strings.EqualFold(savefilepath, "") {
|
||||
path = this.Path + data
|
||||
path = that.Path + data
|
||||
} else {
|
||||
path = savefilepath + data
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func (this *Upload) UpFile(Request *http.Request, fieldName, savefilepath, saveP
|
||||
}
|
||||
}
|
||||
filename := time.Unix(int64(t), 0).Format("2006-01-02-15-22-25") + ObjToStr(Rand(6))
|
||||
filePath = path + "/" + filename + this.Path
|
||||
filePath = path + "/" + filename + that.Path
|
||||
} else {
|
||||
filePath = savePath
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"github.com/silenceper/wechat/v2"
|
||||
"github.com/silenceper/wechat/v2/cache"
|
||||
"github.com/silenceper/wechat/v2/officialaccount"
|
||||
h5config "github.com/silenceper/wechat/v2/officialaccount/config"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/js"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/oauth"
|
||||
)
|
||||
|
||||
//基于此文档开发
|
||||
//https://github.com/silenceper/wechat/blob/v2/doc/api/officialaccount.md
|
||||
type h5Program struct {
|
||||
Memory *cache.Memory
|
||||
Config *h5config.Config
|
||||
*officialaccount.OfficialAccount
|
||||
weixin *wechat.Wechat //微信登录实例
|
||||
}
|
||||
|
||||
var H5Program = h5Program{}
|
||||
|
||||
// Init 初始化
|
||||
func (that *h5Program) Init(appid string, appsecret string) {
|
||||
that.weixin = wechat.NewWechat()
|
||||
that.Memory = cache.NewMemory()
|
||||
that.Config = &h5config.Config{
|
||||
AppID: appid,
|
||||
AppSecret: appsecret,
|
||||
//Token: "xxx",
|
||||
//EncodingAESKey: "xxxx",
|
||||
Cache: that.Memory,
|
||||
}
|
||||
that.OfficialAccount = that.weixin.GetOfficialAccount(that.Config)
|
||||
}
|
||||
|
||||
// GetUserInfo 获取用户信息
|
||||
func (that *h5Program) GetUserInfo(code string) (appid string, resToken oauth.ResAccessToken, userInfo oauth.UserInfo, err error) {
|
||||
auth := that.GetOauth()
|
||||
//weixin.GetOpenPlatform()
|
||||
resToken, err = auth.GetUserAccessToken(code)
|
||||
if err != nil {
|
||||
|
||||
return auth.AppID, resToken, userInfo, err
|
||||
}
|
||||
|
||||
//getUserInfo
|
||||
userInfo, err = auth.GetUserInfo(resToken.AccessToken, resToken.OpenID, "")
|
||||
if err != nil {
|
||||
return auth.AppID, resToken, userInfo, err
|
||||
}
|
||||
|
||||
return auth.AppID, resToken, userInfo, err
|
||||
}
|
||||
|
||||
// GetSignUrl js url签名
|
||||
func (that *h5Program) GetSignUrl(signUrl string) (*js.Config, error) {
|
||||
|
||||
js := that.OfficialAccount.GetJs()
|
||||
cfg1, e := js.GetConfig(signUrl)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
return cfg1, nil
|
||||
}
|
||||
|
||||
// GetSignUrl js url签名
|
||||
//func (that *h5Program) GetJsPay(signUrl string) (*js.Config, error) {
|
||||
// //
|
||||
// //js := that.OfficialAccount().GetJs()
|
||||
// //
|
||||
// //cfg1, e := js.GetConfig(signUrl)
|
||||
// //if e != nil {
|
||||
// // return nil, e
|
||||
// //}
|
||||
//
|
||||
// return cfg1, nil
|
||||
//}
|
||||
@@ -0,0 +1,66 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/silenceper/wechat/v2"
|
||||
"github.com/silenceper/wechat/v2/cache"
|
||||
"github.com/silenceper/wechat/v2/miniprogram"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/auth"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/config"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/encryptor"
|
||||
)
|
||||
|
||||
type miniProgram struct {
|
||||
Memory *cache.Memory
|
||||
Config *config.Config
|
||||
weixin *wechat.Wechat //微信登录实例
|
||||
*miniprogram.MiniProgram
|
||||
}
|
||||
|
||||
var MiniProgram = miniProgram{}
|
||||
|
||||
// Init 初始化
|
||||
func (that *miniProgram) Init(appid string, appsecret string) {
|
||||
|
||||
that.weixin = wechat.NewWechat()
|
||||
that.Memory = cache.NewMemory()
|
||||
that.Config = &config.Config{
|
||||
AppID: appid,
|
||||
AppSecret: appsecret,
|
||||
//Token: "xxx",
|
||||
//EncodingAESKey: "xxxx",
|
||||
Cache: that.Memory,
|
||||
}
|
||||
that.MiniProgram = that.weixin.GetMiniProgram(that.Config)
|
||||
}
|
||||
|
||||
func (that *miniProgram) GetBaseUserInfo(code string) (appid string, re auth.ResCode2Session, err error) {
|
||||
appid = that.Config.AppID
|
||||
a := that.GetAuth()
|
||||
re, err = a.Code2Session(code)
|
||||
|
||||
if err != nil {
|
||||
return appid, re, err
|
||||
}
|
||||
|
||||
return appid, re, err
|
||||
}
|
||||
|
||||
func (that *miniProgram) GetPhoneNumber(sessionkey, encryptedData, iv string) (appid string, re *encryptor.PlainData, err error) {
|
||||
appid = that.Config.AppID
|
||||
|
||||
if sessionkey == "" || encryptedData == "" || iv == "" {
|
||||
return appid, re, errors.New("参数不足")
|
||||
}
|
||||
|
||||
eny := that.GetEncryptor()
|
||||
|
||||
re, err = eny.Decrypt(sessionkey, encryptedData, iv)
|
||||
|
||||
if err != nil {
|
||||
return appid, re, err
|
||||
}
|
||||
|
||||
return appid, re, err
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-pay/gopay"
|
||||
"github.com/go-pay/gopay/wechat/v3"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
//基于此文档开发
|
||||
//https://github.com/silenceper/wechat/blob/v2/doc/api/officialaccount.md
|
||||
type wxpay struct {
|
||||
client *wechat.ClientV3
|
||||
ctx context.Context
|
||||
apiV3Key string
|
||||
MchId string
|
||||
}
|
||||
|
||||
var WxPay = wxpay{}
|
||||
|
||||
// Init 初始化
|
||||
func (that *wxpay) Init(MchId, SerialNo, APIv3Key, PrivateKey string) {
|
||||
client, err := wechat.NewClientV3(MchId, SerialNo, APIv3Key, PrivateKey)
|
||||
if err != nil {
|
||||
//xlog.Error(err)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
that.client = client
|
||||
that.apiV3Key = APIv3Key
|
||||
that.MchId = MchId
|
||||
// 设置微信平台API证书和序列号(如开启自动验签,请忽略此步骤)
|
||||
//client.SetPlatformCert([]byte(""), "")
|
||||
that.ctx = context.Background()
|
||||
// 启用自动同步返回验签,并定时更新微信平台API证书(开启自动验签时,无需单独设置微信平台API证书和序列号)
|
||||
err = client.AutoVerifySign()
|
||||
if err != nil {
|
||||
//xlog.Error(err)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 打开Debug开关,输出日志,默认是关闭的
|
||||
client.DebugSwitch = gopay.DebugOn
|
||||
}
|
||||
|
||||
// GetUserInfo 获取用户信息
|
||||
func (that *wxpay) GetJsOrder(money int64, appid, openid, name, tradeNo, notifyUrl string) (jsApiParams *wechat.JSAPIPayParams, err error) {
|
||||
fmt.Println("dasdas", money, appid, name, tradeNo, notifyUrl)
|
||||
PrepayId, err := that.getPrepayId(money, appid, that.MchId, openid, name, tradeNo, notifyUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//小程序
|
||||
jsapi, err := that.client.PaySignOfJSAPI(appid, PrepayId)
|
||||
return jsapi, err
|
||||
}
|
||||
|
||||
func (that *wxpay) CallbackJsOrder(req *http.Request) (*wechat.V3DecryptResult, error) {
|
||||
|
||||
notifyReq, err := wechat.V3ParseNotify(req)
|
||||
if err != nil {
|
||||
//xlog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// wxPublicKey 通过 client.WxPublicKey() 获取
|
||||
err = notifyReq.VerifySignByPK(that.client.WxPublicKey())
|
||||
if err != nil {
|
||||
//xlog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ========异步通知敏感信息解密========
|
||||
// 普通支付通知解密
|
||||
result, err := notifyReq.DecryptCipherText(that.apiV3Key)
|
||||
|
||||
//that.client.V3TransactionQueryOrder(that.ctx,result.BankType,result.OR)
|
||||
|
||||
return result, err
|
||||
|
||||
// 合单支付通知解密
|
||||
//result, err := notifyReq.DecryptCombineCipherText(apiV3Key)
|
||||
//// 退款通知解密
|
||||
//result, err := notifyReq.DecryptRefundCipherText(apiV3Key)
|
||||
|
||||
// ========异步通知应答========
|
||||
// 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。
|
||||
// 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。
|
||||
|
||||
// 此写法是 gin 框架返回微信的写法
|
||||
//c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"})
|
||||
//
|
||||
//// 此写法是 echo 框架返回微信的写法
|
||||
//return c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"})
|
||||
}
|
||||
|
||||
// GetUserInfo 获取用户信息
|
||||
//func (that *wxpay) GetMiniOrder(money int64,appid,name,tradeNo,notifyUrl string) (jsApiParams *wechat.AppletParams,err error){
|
||||
//
|
||||
// PrepayId,err:=that.getPrepayId(money,name,tradeNo,notifyUrl)
|
||||
// if err!=nil{
|
||||
// return nil,err
|
||||
// }
|
||||
//
|
||||
// //小程序
|
||||
// applet, err := that.client.PaySignOfApplet(appid,PrepayId)
|
||||
//
|
||||
// return applet,err
|
||||
//}
|
||||
|
||||
func (that *wxpay) getPrepayId(money int64, appid, mchid, openid, name, tradeNo, notifyUrl string) (prepayid string, err error) {
|
||||
expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
|
||||
// 初始化 BodyMap
|
||||
bm := make(gopay.BodyMap)
|
||||
bm.Set("appid", appid).
|
||||
Set("mchid", mchid).
|
||||
//Set("sub_mchid", "sub_mchid").
|
||||
Set("description", name).
|
||||
Set("out_trade_no", tradeNo).
|
||||
Set("time_expire", expire).
|
||||
Set("notify_url", notifyUrl).
|
||||
SetBodyMap("amount", func(bm gopay.BodyMap) {
|
||||
bm.Set("total", money).
|
||||
Set("currency", "CNY")
|
||||
}).
|
||||
SetBodyMap("payer", func(bm gopay.BodyMap) {
|
||||
bm.Set("openid", openid)
|
||||
})
|
||||
//ctx:=context.Context()
|
||||
|
||||
wxRsp, err := that.client.V3TransactionJsapi(that.ctx, bm)
|
||||
fmt.Println("获取PrepayId", wxRsp, err)
|
||||
if err != nil {
|
||||
//xlog.Error(err)
|
||||
return "", err
|
||||
}
|
||||
return wxRsp.Response.PrepayId, nil
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
)
|
||||
|
||||
var adminCtr = Ctr{
|
||||
"token": func(this *Context) {
|
||||
this.Display(0, this.SessionId)
|
||||
},
|
||||
"test": func(this *Context) {
|
||||
this.Session("id", this.SessionId)
|
||||
},
|
||||
//自带的登录
|
||||
"login": func(this *Context) {
|
||||
|
||||
name := this.Req.FormValue("name")
|
||||
pwd := this.Req.FormValue("password")
|
||||
if len(name) < 2 ||
|
||||
len(pwd) < 3 {
|
||||
this.Display(3, "数据校验不通过")
|
||||
}
|
||||
where := Map{"password": Md5(pwd)}
|
||||
if len(name) == 11 {
|
||||
where["phone"] = name
|
||||
} else {
|
||||
where["name"] = name
|
||||
}
|
||||
|
||||
admin := this.Db.Get("admin", "*", Map{"AND": where})
|
||||
if admin == nil {
|
||||
this.Display(4, "账户密码错误")
|
||||
return
|
||||
}
|
||||
|
||||
this.Session("id", admin.GetCeilInt("id"))
|
||||
admin["password"] = nil
|
||||
this.Display(0, admin)
|
||||
|
||||
},
|
||||
"info": func(this *Context) {
|
||||
admin := this.Db.Get("admin", "*", Map{"id": this.Session("id").ToInt()})
|
||||
|
||||
if admin == nil {
|
||||
this.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
admin["password"] = nil
|
||||
|
||||
this.Display(0, admin)
|
||||
},
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
)
|
||||
|
||||
// Project 管理端项目
|
||||
var Project = Proj{
|
||||
//"user": UserCtr,
|
||||
"product_spot_check": product_spot_checkCtr,
|
||||
"product": productCtr,
|
||||
"admin": adminCtr,
|
||||
"sms": Sms,
|
||||
"material": materialCtr,
|
||||
"material_inout": material_inoutCtr,
|
||||
"produce_product": produce_productCtr,
|
||||
"produce": produceCtr,
|
||||
"product_line": product_lineCtr,
|
||||
}
|
||||
|
||||
//生成随机码的4位随机数
|
||||
func getCode() string {
|
||||
//res := ""
|
||||
//for i := 0; i < 4; i++ {
|
||||
res := ObjToStr(RandX(1000, 9999))
|
||||
//}
|
||||
return res
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var materialCtr = Ctr{
|
||||
"info": func(that *Context) {
|
||||
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||
str, inData := that.MakeCode.Info(that.RouterString[1], data, that.Db)
|
||||
where := Map{"id": that.RouterString[2]}
|
||||
|
||||
if len(inData) == 1 {
|
||||
inData["id"] = where["id"]
|
||||
where = Map{"AND": inData}
|
||||
} else if len(inData) > 1 {
|
||||
where["OR"] = inData
|
||||
where = Map{"AND": where}
|
||||
}
|
||||
|
||||
re := that.Db.Get(that.RouterString[1], str, where)
|
||||
|
||||
if re == nil {
|
||||
that.Display(4, "找不到对应信息")
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range re {
|
||||
column := that.MakeCode.TableColumns[that.RouterString[1]][k]
|
||||
if column == nil {
|
||||
continue
|
||||
}
|
||||
if (column["list"] == nil || column.GetBool("list")) && column.GetString("link") != "" {
|
||||
re[column.GetString("link")] = that.Db.Get(column.GetString("link"), "id,"+column.GetString("value"), Map{"id": v})
|
||||
}
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"add": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
name := that.Req.FormValue("name")
|
||||
img := that.Req.FormValue("img")
|
||||
validity := ObjToInt(that.Req.FormValue("validity"))
|
||||
num := ObjToInt(that.Req.FormValue("num"))
|
||||
rule := that.Req.FormValue("rule")
|
||||
content := that.Req.FormValue("content")
|
||||
description := that.Req.FormValue("description")
|
||||
if name == "" || rule == "" {
|
||||
that.Display(3, "参数不足,请补充参数")
|
||||
return
|
||||
}
|
||||
|
||||
data := Map{
|
||||
"name": name,
|
||||
"img": img,
|
||||
"rule": rule,
|
||||
"admin_id": adminID,
|
||||
"count": 0,
|
||||
"used": 0,
|
||||
"saved": 0,
|
||||
"num": num,
|
||||
"validity": validity,
|
||||
"description": description,
|
||||
"content": content,
|
||||
"create_time": time.Now().Unix(),
|
||||
"modify_time": time.Now().Unix(),
|
||||
}
|
||||
|
||||
id := that.Db.Insert("material", data)
|
||||
if id == 0 {
|
||||
that.Display(4, "添加材料失败,请重新添加")
|
||||
return
|
||||
}
|
||||
|
||||
data["id"] = id
|
||||
|
||||
that.Display(0, data)
|
||||
},
|
||||
"update": func(that *Context) {
|
||||
inData := that.MakeCode.Edit(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "没有找到要更新的数据")
|
||||
return
|
||||
}
|
||||
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetString("index") != "" {
|
||||
Index := that.Db.Get(that.RouterString[1], "`index`", Map{"id": that.RouterString[2]})
|
||||
parentIndex := that.Db.Get(that.RouterString[1], "`index`", Map{"id": inData.Get("parent_id")})
|
||||
inData["index"] = parentIndex.GetString("index") + that.RouterString[2] + ","
|
||||
|
||||
childNodes := that.Db.Select(that.RouterString[1], "id,`index`", Map{"index[~]": "," + that.RouterString[2] + ","})
|
||||
|
||||
for _, v := range childNodes {
|
||||
v["index"] = strings.Replace(v.GetString("index"), Index.GetString("index"), inData.GetString("index"), -1)
|
||||
that.Db.Update(that.RouterString[1], Map{"index": v["index"]}, Map{"id": v.GetCeilInt("id")})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
re := that.Db.Update(that.RouterString[1], inData, Map{"id": that.RouterString[2]})
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "更新数据失败")
|
||||
return
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"inout": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
data := ObjToMap(that.Req.FormValue("data"))
|
||||
|
||||
texts := data.GetSlice("text")
|
||||
textData := []Map{}
|
||||
for k, _ := range texts {
|
||||
v := texts.GetString(k)
|
||||
if len(v) < 4 {
|
||||
continue
|
||||
}
|
||||
vs := that.Db.Select("material", "name,id,content,rule,num", Map{"content[~]": v[:len(v)/2]})
|
||||
for _, v1 := range vs {
|
||||
if len(textData) == 0 {
|
||||
textData = append(textData, v1)
|
||||
}
|
||||
for _, vt := range textData {
|
||||
if v1.GetString("id") != vt.GetString("id") {
|
||||
|
||||
add := true
|
||||
for _, vt1 := range textData {
|
||||
if vt1.GetCeilInt("id") == v1.GetCeilInt("id") {
|
||||
add = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if add {
|
||||
v1["count"] = 1
|
||||
textData = append(textData, v1)
|
||||
}
|
||||
} else {
|
||||
vt["count"] = vt.GetCeilInt("count") + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
qrcode := data.GetSlice("qrcode")
|
||||
for k, _ := range qrcode {
|
||||
v := qrcode.GetString(k)
|
||||
if len(v) < 4 {
|
||||
continue
|
||||
}
|
||||
vs := that.Db.Select("material", "name,id,content,rule,num", Map{"content[~]": v[:len(v)/2]})
|
||||
for _, v1 := range vs {
|
||||
if len(textData) == 0 {
|
||||
textData = append(textData, v1)
|
||||
}
|
||||
for _, vt := range textData {
|
||||
if v1.GetString("id") != vt.GetString("id") {
|
||||
v1["count"] = 1
|
||||
textData = append(textData, v1)
|
||||
} else {
|
||||
vt["count"] = vt.GetCeilInt("count") + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
that.Display(0, textData)
|
||||
|
||||
},
|
||||
"search": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
page := ObjToInt(that.Req.FormValue("page"))
|
||||
pageSize := ObjToInt(that.Req.FormValue("pageSize"))
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
leftJoin := Map{"[><]admin": "material.admin_id=admin.id"}
|
||||
columnStr := "material.id,material.name,material.img,material.count,material.used,material.saved,material.admin_id,admin.name AS admin_name,material.modify_time,material.state"
|
||||
where := Map{"ORDER": "modify_time DESC"}
|
||||
count := that.Db.Count("material", where)
|
||||
reData := that.Db.Page(page, pageSize).
|
||||
PageSelect("material", leftJoin, columnStr, where)
|
||||
|
||||
that.Display(0, Map{"count": count, "data": reData})
|
||||
},
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var material_inoutCtr = Ctr{
|
||||
"info": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
id := ObjToInt(that.Req.FormValue("id"))
|
||||
if id == 0 {
|
||||
that.Display(3, "请求参数不足,请检查参数")
|
||||
return
|
||||
}
|
||||
|
||||
re := that.Db.Get("material_inout", "*", Map{"id": id})
|
||||
|
||||
if re == nil {
|
||||
that.Display(4, "找不到对应信息")
|
||||
return
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"add": func(that *Context) {
|
||||
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
img := that.Req.FormValue("img")
|
||||
rule := that.Req.FormValue("rule")
|
||||
materialId := ObjToInt(that.Req.FormValue("material_id"))
|
||||
produceId := ObjToInt(that.Req.FormValue("produce_id"))
|
||||
count := ObjToInt(that.Req.FormValue("num"))
|
||||
state := ObjToInt(that.Req.FormValue("state"))
|
||||
|
||||
content := that.Req.FormValue("content")
|
||||
description := that.Req.FormValue("description")
|
||||
|
||||
if rule == "" || materialId == 0 || count == 0 {
|
||||
that.Display(3, "参数不足,请补充参数")
|
||||
return
|
||||
}
|
||||
count1 := count
|
||||
if state > 0 {
|
||||
count = -count
|
||||
}
|
||||
|
||||
produce_material := that.Db.Get("produce_material", "id", Map{"AND": Map{"produce_id": produceId, "material_id": materialId}})
|
||||
if produce_material == nil {
|
||||
that.Db.Insert("produce_material", Map{"produce_id": produceId,
|
||||
"create_time": time.Now().Unix(),
|
||||
"modify_time": time.Now().Unix(),
|
||||
"admin_id": adminID,
|
||||
"material_id": materialId})
|
||||
}
|
||||
if state == 0 {
|
||||
|
||||
that.Db.Update("material", Map{"count[#]": "count+" + ObjToStr(count), "saved[#]": "saved+" + ObjToStr(count)}, Map{"id": materialId})
|
||||
} else {
|
||||
that.Db.Update("material", Map{"count[#]": "count" + ObjToStr(count), "used[#]": "used+" + ObjToStr(-count)}, Map{"id": materialId})
|
||||
}
|
||||
|
||||
material := that.Db.Get("material", "*", Map{"id": materialId})
|
||||
data := Map{
|
||||
"img": img,
|
||||
"rule": rule,
|
||||
"admin_id": adminID,
|
||||
"material_id": materialId,
|
||||
"count": count1,
|
||||
"saved": material.GetCeilInt("count"),
|
||||
"create_time": time.Now().Unix(),
|
||||
"modify_time": time.Now().Unix(),
|
||||
"produce_id": produceId,
|
||||
"description": description,
|
||||
"content": content,
|
||||
"state": state,
|
||||
}
|
||||
id := that.Db.Insert("material_inout", data)
|
||||
|
||||
if id == 0 {
|
||||
that.Display(4, "添加出入库记录失败,请重新添加")
|
||||
return
|
||||
}
|
||||
|
||||
data["id"] = id
|
||||
|
||||
that.Display(0, data)
|
||||
},
|
||||
"update": func(that *Context) {
|
||||
inData := that.MakeCode.Edit(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "没有找到要更新的数据")
|
||||
return
|
||||
}
|
||||
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetString("index") != "" {
|
||||
Index := that.Db.Get(that.RouterString[1], "`index`", Map{"id": that.RouterString[2]})
|
||||
parentIndex := that.Db.Get(that.RouterString[1], "`index`", Map{"id": inData.Get("parent_id")})
|
||||
inData["index"] = parentIndex.GetString("index") + that.RouterString[2] + ","
|
||||
|
||||
childNodes := that.Db.Select(that.RouterString[1], "id,`index`", Map{"index[~]": "," + that.RouterString[2] + ","})
|
||||
|
||||
for _, v := range childNodes {
|
||||
v["index"] = strings.Replace(v.GetString("index"), Index.GetString("index"), inData.GetString("index"), -1)
|
||||
that.Db.Update(that.RouterString[1], Map{"index": v["index"]}, Map{"id": v.GetCeilInt("id")})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
re := that.Db.Update(that.RouterString[1], inData, Map{"id": that.RouterString[2]})
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "更新数据失败")
|
||||
return
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"remove": func(that *Context) {
|
||||
inData := that.MakeCode.Delete(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "请求参数不足")
|
||||
return
|
||||
}
|
||||
re := int64(0)
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetSlice("index") != nil {
|
||||
re = that.Db.Delete(that.RouterString[1], Map{"index[~]": "," + that.RouterString[2] + ","})
|
||||
} else {
|
||||
re = that.Db.Delete(that.RouterString[1], Map{"id": that.RouterString[2]})
|
||||
}
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "删除数据失败")
|
||||
return
|
||||
}
|
||||
that.Display(0, "删除成功")
|
||||
},
|
||||
|
||||
"search": func(that *Context) {
|
||||
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
page := ObjToInt(that.Req.FormValue("page"))
|
||||
pageSize := ObjToInt(that.Req.FormValue("pageSize"))
|
||||
materialId := ObjToInt(that.Req.FormValue("id"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
columnStr := "material_inout.id,material_inout.material_id,material.name,material_inout.img,material_inout.count,material_inout.saved,material_inout.admin_id,admin.name AS admin_name,material_inout.modify_time,material_inout.state"
|
||||
leftJoin := Map{"[><]material": "material_inout.material_id=material.id",
|
||||
"[><]admin": "material_inout.admin_id=admin.id",
|
||||
}
|
||||
where := Map{"ORDER": "modify_time DESC"}
|
||||
|
||||
if materialId != 0 {
|
||||
where["material_id"] = materialId
|
||||
}
|
||||
count := that.Db.Count("material_inout", where)
|
||||
reData := that.Db.Page(page, pageSize).
|
||||
PageSelect("material_inout", leftJoin, columnStr, where)
|
||||
|
||||
that.Display(0, Map{"count": count, "data": reData})
|
||||
},
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var produceCtr = Ctr{
|
||||
"info": func(that *Context) {
|
||||
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||
str, inData := that.MakeCode.Info(that.RouterString[1], data, that.Db)
|
||||
where := Map{"id": that.RouterString[2]}
|
||||
|
||||
if len(inData) == 1 {
|
||||
inData["id"] = where["id"]
|
||||
where = Map{"AND": inData}
|
||||
} else if len(inData) > 1 {
|
||||
where["OR"] = inData
|
||||
where = Map{"AND": where}
|
||||
}
|
||||
|
||||
re := that.Db.Get(that.RouterString[1], str, where)
|
||||
|
||||
if re == nil {
|
||||
that.Display(4, "找不到对应信息")
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range re {
|
||||
column := that.MakeCode.TableColumns[that.RouterString[1]][k]
|
||||
if column == nil {
|
||||
continue
|
||||
}
|
||||
if (column["list"] == nil || column.GetBool("list")) && column.GetString("link") != "" {
|
||||
re[column.GetString("link")] = that.Db.Get(column.GetString("link"), "id,"+column.GetString("value"), Map{"id": v})
|
||||
}
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"add": func(that *Context) {
|
||||
inData := that.MakeCode.Add(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "请求参数不足")
|
||||
return
|
||||
}
|
||||
|
||||
re := that.Db.Insert(that.RouterString[1], inData)
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "无法插入对应数据")
|
||||
return
|
||||
}
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetString("index") != "" {
|
||||
index := that.Db.Get(that.RouterString[1], "`index`", Map{"id": inData.Get("parent_id")})
|
||||
inData["index"] = index.GetString("index") + ObjToStr(re) + ","
|
||||
that.Db.Update(that.RouterString[1], Map{"index": inData["index"]}, Map{"id": re})
|
||||
} else if inData.GetString("index") != "" {
|
||||
inData["index"] = "," + ObjToStr(re) + ","
|
||||
that.Db.Update(that.RouterString[1], Map{"index": inData["index"]}, Map{"id": re})
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"update": func(that *Context) {
|
||||
inData := that.MakeCode.Edit(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "没有找到要更新的数据")
|
||||
return
|
||||
}
|
||||
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetString("index") != "" {
|
||||
Index := that.Db.Get(that.RouterString[1], "`index`", Map{"id": that.RouterString[2]})
|
||||
parentIndex := that.Db.Get(that.RouterString[1], "`index`", Map{"id": inData.Get("parent_id")})
|
||||
inData["index"] = parentIndex.GetString("index") + that.RouterString[2] + ","
|
||||
|
||||
childNodes := that.Db.Select(that.RouterString[1], "id,`index`", Map{"index[~]": "," + that.RouterString[2] + ","})
|
||||
|
||||
for _, v := range childNodes {
|
||||
v["index"] = strings.Replace(v.GetString("index"), Index.GetString("index"), inData.GetString("index"), -1)
|
||||
that.Db.Update(that.RouterString[1], Map{"index": v["index"]}, Map{"id": v.GetCeilInt("id")})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
re := that.Db.Update(that.RouterString[1], inData, Map{"id": that.RouterString[2]})
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "更新数据失败")
|
||||
return
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"remove": func(that *Context) {
|
||||
inData := that.MakeCode.Delete(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "请求参数不足")
|
||||
return
|
||||
}
|
||||
re := int64(0)
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetSlice("index") != nil {
|
||||
re = that.Db.Delete(that.RouterString[1], Map{"index[~]": "," + that.RouterString[2] + ","})
|
||||
} else {
|
||||
re = that.Db.Delete(that.RouterString[1], Map{"id": that.RouterString[2]})
|
||||
}
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "删除数据失败")
|
||||
return
|
||||
}
|
||||
that.Display(0, "删除成功")
|
||||
},
|
||||
"check": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
data := ObjToMap(that.Req.FormValue("data"))
|
||||
|
||||
texts := data.GetSlice("text")
|
||||
textData := []Map{}
|
||||
for k, _ := range texts {
|
||||
v := texts.GetString(k)
|
||||
if len(v) < 3 {
|
||||
continue
|
||||
}
|
||||
vs := that.Db.Select("produce", Map{"[>]product": "produce.product_id=product.id"}, "produce.name,produce.id,produce.product_id,product.name AS product_name,product.rule_check,product.rule_spot_check", Map{"produce.sn[~]": v[:len(v)/2+1]})
|
||||
for _, v1 := range vs {
|
||||
if len(textData) == 0 {
|
||||
textData = append(textData, v1)
|
||||
}
|
||||
for _, vt := range textData {
|
||||
if v1.GetString("id") != vt.GetString("id") {
|
||||
|
||||
add := true
|
||||
for _, vt1 := range textData {
|
||||
if vt1.GetCeilInt("id") == v1.GetCeilInt("id") {
|
||||
add = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if add {
|
||||
v1["count"] = 1
|
||||
textData = append(textData, v1)
|
||||
}
|
||||
} else {
|
||||
vt["count"] = vt.GetCeilInt("count") + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
qrcode := data.GetSlice("qrcode")
|
||||
for k, _ := range qrcode {
|
||||
v := qrcode.GetString(k)
|
||||
if len(v) < 3 {
|
||||
continue
|
||||
}
|
||||
vs := that.Db.Select("produce", Map{"[>]product": "produce.product_id=product.id"}, "produce.name,produce.id,produce.product_id,product.name AS product_name,product.rule_check,product.rule_spot_check", Map{"produce.sn[~]": v[:len(v)/2+1]})
|
||||
for _, v1 := range vs {
|
||||
if len(textData) == 0 {
|
||||
textData = append(textData, v1)
|
||||
}
|
||||
for _, vt := range textData {
|
||||
if v1.GetString("id") != vt.GetString("id") {
|
||||
v1["count"] = 1
|
||||
textData = append(textData, v1)
|
||||
} else {
|
||||
vt["count"] = vt.GetCeilInt("count") + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
that.Display(0, textData)
|
||||
|
||||
},
|
||||
"search": func(that *Context) {
|
||||
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
columnStr := "produce.id,produce.sn,produce.name,produce.state,produce.product_id,product.name AS product_name"
|
||||
where := Map{"produce.state[!]": 0, "ORDER": "produce.modify_time DESC"}
|
||||
|
||||
reData := that.Db.Select("produce", Map{"[>]product": "produce.product_id=product.id"}, columnStr, where)
|
||||
|
||||
that.Display(0, reData)
|
||||
},
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
var produce_productCtr = Ctr{
|
||||
"info": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
id := ObjToInt(that.Req.FormValue("id"))
|
||||
sn := that.Req.FormValue("sn")
|
||||
if id == 0 && sn == "" {
|
||||
that.Display(3, "请求参数不足,请检查参数")
|
||||
return
|
||||
}
|
||||
|
||||
where := Map{}
|
||||
if id != 0 {
|
||||
where["produce_product.id"] = id
|
||||
} else {
|
||||
where["produce_product.sn"] = sn
|
||||
}
|
||||
|
||||
re := that.Db.Get("produce_product",
|
||||
|
||||
Map{"[><]product": "produce_product.product_id=product.id",
|
||||
"[><]produce": "produce_product.produce_id=produce.id",
|
||||
},
|
||||
"produce_product.id,produce_product.product_id,product.name AS product_name,"+
|
||||
"produce_product.modify_time,produce_product.state,product.rule_spot_check,produce_product.produce_id,produce.name AS produce_name", where)
|
||||
|
||||
if re == nil {
|
||||
that.Display(4, "找不到对应信息")
|
||||
return
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"add": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
sn := that.Req.FormValue("sn")
|
||||
product_id := ObjToInt(that.Req.FormValue("product_id"))
|
||||
produce_id := ObjToInt(that.Req.FormValue("produce_id"))
|
||||
product_line_id := ObjToInt(that.Req.FormValue("product_line_id"))
|
||||
//state := ObjToInt(that.Req.FormValue("state"))
|
||||
//rule_check := that.Req.FormValue("rule_check")
|
||||
//description := that.Req.FormValue("description")
|
||||
if sn == "" {
|
||||
that.Display(3, "参数不足,请补充参数")
|
||||
return
|
||||
}
|
||||
|
||||
data := Map{
|
||||
|
||||
"sn": sn,
|
||||
"product_id": product_id,
|
||||
"produce_id": produce_id,
|
||||
"create_time": time.Now().Unix(),
|
||||
"modify_time": time.Now().Unix(),
|
||||
}
|
||||
|
||||
data1 := ObjToMap(data.ToJsonString())
|
||||
data1["product_line_id"] = product_line_id
|
||||
id := that.Db.Insert("produce_product", data1)
|
||||
if id == 0 {
|
||||
that.Display(4, "添加新成品失败,请重新添加")
|
||||
return
|
||||
}
|
||||
|
||||
//data["id"] = id
|
||||
//data["rule"] = rule_check
|
||||
//data["produce_product_id"] = id
|
||||
//data["state"] = state
|
||||
//data["description"] = description
|
||||
//id = that.Db.Insert("product_check", data)
|
||||
//if id == 0 {
|
||||
// that.Display(4, "添加质检失败,请重新添加")
|
||||
// return
|
||||
//}
|
||||
that.Display(0, data)
|
||||
},
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
var productCtr = Ctr{
|
||||
"info": func(that *Context) {
|
||||
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||
str, inData := that.MakeCode.Info(that.RouterString[1], data, that.Db)
|
||||
where := Map{"id": that.RouterString[2]}
|
||||
|
||||
if len(inData) == 1 {
|
||||
inData["id"] = where["id"]
|
||||
where = Map{"AND": inData}
|
||||
} else if len(inData) > 1 {
|
||||
where["OR"] = inData
|
||||
where = Map{"AND": where}
|
||||
}
|
||||
|
||||
re := that.Db.Get(that.RouterString[1], str, where)
|
||||
|
||||
if re == nil {
|
||||
that.Display(4, "找不到对应信息")
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range re {
|
||||
column := that.MakeCode.TableColumns[that.RouterString[1]][k]
|
||||
if column == nil {
|
||||
continue
|
||||
}
|
||||
if (column["list"] == nil || column.GetBool("list")) && column.GetString("link") != "" {
|
||||
re[column.GetString("link")] = that.Db.Get(column.GetString("link"), "id,"+column.GetString("value"), Map{"id": v})
|
||||
}
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"add": func(that *Context) {
|
||||
inData := that.MakeCode.Add(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "请求参数不足")
|
||||
return
|
||||
}
|
||||
|
||||
re := that.Db.Insert(that.RouterString[1], inData)
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "无法插入对应数据")
|
||||
return
|
||||
}
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetString("index") != "" {
|
||||
index := that.Db.Get(that.RouterString[1], "`index`", Map{"id": inData.Get("parent_id")})
|
||||
inData["index"] = index.GetString("index") + ObjToStr(re) + ","
|
||||
that.Db.Update(that.RouterString[1], Map{"index": inData["index"]}, Map{"id": re})
|
||||
} else if inData.GetString("index") != "" {
|
||||
inData["index"] = "," + ObjToStr(re) + ","
|
||||
that.Db.Update(that.RouterString[1], Map{"index": inData["index"]}, Map{"id": re})
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"update": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
id := ObjToInt(that.Req.FormValue("id"))
|
||||
//抽检更新
|
||||
ruleSpotCheck := that.Req.FormValue("rule_spot_check")
|
||||
if ruleSpotCheck != "" {
|
||||
spotCheckPercentage := ObjToInt(that.Req.FormValue("spot_check_percentage"))
|
||||
if id == 0 || ruleSpotCheck == "" {
|
||||
that.Display(3, "请求参数不足,请检查参数")
|
||||
return
|
||||
}
|
||||
|
||||
re := that.Db.Update("product", Map{"rule_spot_check": ruleSpotCheck, "spot_check_percentage": spotCheckPercentage, "modify_time": time.Now().Unix()}, Map{"id": id})
|
||||
if re == 0 {
|
||||
that.Display(4, "更新失败,无法更新抽检参数")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
//质检更新
|
||||
ruleCheck := that.Req.FormValue("rule_check")
|
||||
if id == 0 || ruleCheck == "" {
|
||||
that.Display(3, "请求参数不足,请检查参数")
|
||||
return
|
||||
}
|
||||
|
||||
re := that.Db.Update("product", Map{"rule_check": ruleCheck, "modify_time": time.Now().Unix()}, Map{"id": id})
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "更新失败,无法更新质检参数")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
that.Display(0, "更新成功")
|
||||
},
|
||||
|
||||
"search": func(that *Context) {
|
||||
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
page := ObjToInt(that.Req.FormValue("page"))
|
||||
pageSize := ObjToInt(that.Req.FormValue("pageSize"))
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
leftJoin := Map{"[><]admin": "product.admin_id=admin.id"}
|
||||
columnStr := "product.id,product.name,product.img,product.count,product.used,product.saved,product.spot_check_count,product.admin_id,admin.name AS admin_name,product.modify_time,product.state"
|
||||
where := Map{"ORDER": "modify_time DESC"}
|
||||
count := that.Db.Count("product", where)
|
||||
reData := that.Db.Page(page, pageSize).
|
||||
PageSelect("product", leftJoin, columnStr, where)
|
||||
|
||||
that.Display(0, Map{"count": count, "data": reData})
|
||||
},
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var product_lineCtr = Ctr{
|
||||
"info": func(that *Context) {
|
||||
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||
str, inData := that.MakeCode.Info(that.RouterString[1], data, that.Db)
|
||||
where := Map{"id": that.RouterString[2]}
|
||||
|
||||
if len(inData) == 1 {
|
||||
inData["id"] = where["id"]
|
||||
where = Map{"AND": inData}
|
||||
} else if len(inData) > 1 {
|
||||
where["OR"] = inData
|
||||
where = Map{"AND": where}
|
||||
}
|
||||
|
||||
re := that.Db.Get(that.RouterString[1], str, where)
|
||||
|
||||
if re == nil {
|
||||
that.Display(4, "找不到对应信息")
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range re {
|
||||
column := that.MakeCode.TableColumns[that.RouterString[1]][k]
|
||||
if column == nil {
|
||||
continue
|
||||
}
|
||||
if (column["list"] == nil || column.GetBool("list")) && column.GetString("link") != "" {
|
||||
re[column.GetString("link")] = that.Db.Get(column.GetString("link"), "id,"+column.GetString("value"), Map{"id": v})
|
||||
}
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"add": func(that *Context) {
|
||||
inData := that.MakeCode.Add(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "请求参数不足")
|
||||
return
|
||||
}
|
||||
|
||||
re := that.Db.Insert(that.RouterString[1], inData)
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "无法插入对应数据")
|
||||
return
|
||||
}
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetString("index") != "" {
|
||||
index := that.Db.Get(that.RouterString[1], "`index`", Map{"id": inData.Get("parent_id")})
|
||||
inData["index"] = index.GetString("index") + ObjToStr(re) + ","
|
||||
that.Db.Update(that.RouterString[1], Map{"index": inData["index"]}, Map{"id": re})
|
||||
} else if inData.GetString("index") != "" {
|
||||
inData["index"] = "," + ObjToStr(re) + ","
|
||||
that.Db.Update(that.RouterString[1], Map{"index": inData["index"]}, Map{"id": re})
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"update": func(that *Context) {
|
||||
inData := that.MakeCode.Edit(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "没有找到要更新的数据")
|
||||
return
|
||||
}
|
||||
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetString("index") != "" {
|
||||
Index := that.Db.Get(that.RouterString[1], "`index`", Map{"id": that.RouterString[2]})
|
||||
parentIndex := that.Db.Get(that.RouterString[1], "`index`", Map{"id": inData.Get("parent_id")})
|
||||
inData["index"] = parentIndex.GetString("index") + that.RouterString[2] + ","
|
||||
|
||||
childNodes := that.Db.Select(that.RouterString[1], "id,`index`", Map{"index[~]": "," + that.RouterString[2] + ","})
|
||||
|
||||
for _, v := range childNodes {
|
||||
v["index"] = strings.Replace(v.GetString("index"), Index.GetString("index"), inData.GetString("index"), -1)
|
||||
that.Db.Update(that.RouterString[1], Map{"index": v["index"]}, Map{"id": v.GetCeilInt("id")})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
re := that.Db.Update(that.RouterString[1], inData, Map{"id": that.RouterString[2]})
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "更新数据失败")
|
||||
return
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"remove": func(that *Context) {
|
||||
inData := that.MakeCode.Delete(that.RouterString[1], that.Req)
|
||||
if inData == nil {
|
||||
that.Display(3, "请求参数不足")
|
||||
return
|
||||
}
|
||||
re := int64(0)
|
||||
//索引管理,便于检索以及权限
|
||||
if inData.Get("parent_id") != nil && inData.GetSlice("index") != nil {
|
||||
re = that.Db.Delete(that.RouterString[1], Map{"index[~]": "," + that.RouterString[2] + ","})
|
||||
} else {
|
||||
re = that.Db.Delete(that.RouterString[1], Map{"id": that.RouterString[2]})
|
||||
}
|
||||
|
||||
if re == 0 {
|
||||
that.Display(4, "删除数据失败")
|
||||
return
|
||||
}
|
||||
that.Display(0, "删除成功")
|
||||
},
|
||||
|
||||
"search": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
//page := ObjToInt(that.Req.FormValue("page"))
|
||||
//pageSize := ObjToInt(that.Req.FormValue("pageSize"))
|
||||
//
|
||||
//if page < 1 {
|
||||
// page = 1
|
||||
//}
|
||||
//if pageSize <= 0 {
|
||||
// pageSize = 10
|
||||
//}
|
||||
|
||||
//leftJoin := Map{"[><]admin": "product.admin_id=admin.id"}
|
||||
|
||||
where := Map{"state": 0, "ORDER": "modify_time DESC"}
|
||||
//count := that.Db.Count("product", where)
|
||||
reData := that.Db.Select("product_line", "*", where)
|
||||
|
||||
that.Display(0, reData)
|
||||
|
||||
},
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
. "../../../hotime/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
var product_spot_checkCtr = Ctr{
|
||||
"info": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
id := ObjToInt(that.Req.FormValue("id"))
|
||||
if id == 0 {
|
||||
that.Display(3, "请求参数不足,请检查参数")
|
||||
return
|
||||
}
|
||||
|
||||
re := that.Db.Get("product_spot_check",
|
||||
|
||||
Map{"[><]product": "product_spot_check.product_id=product.id",
|
||||
"[><]produce": "product_spot_check.produce_id=produce.id",
|
||||
},
|
||||
"id,img,product_id,product.name AS product_name,admin_id,"+
|
||||
"modify_time,state,rule,produce_id,produce.name AS produce_name", Map{"id": id})
|
||||
|
||||
if re == nil {
|
||||
that.Display(4, "找不到对应信息")
|
||||
return
|
||||
}
|
||||
|
||||
that.Display(0, re)
|
||||
},
|
||||
"add": func(that *Context) {
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
|
||||
//img := that.Req.FormValue("img")
|
||||
sn := that.Req.FormValue("sn")
|
||||
rule := that.Req.FormValue("rule_spot_check")
|
||||
description := that.Req.FormValue("description")
|
||||
produceProductId := ObjToInt(that.Req.FormValue("produce_product_id"))
|
||||
|
||||
//count := ObjToInt(that.Req.FormValue("count"))
|
||||
state := ObjToInt(that.Req.FormValue("state"))
|
||||
if rule == "" || produceProductId == 0 {
|
||||
that.Display(3, "参数不足,请补充参数")
|
||||
return
|
||||
}
|
||||
|
||||
produceProduct := that.Db.Get("produce_product", "*", Map{"id": produceProductId})
|
||||
if produceProduct == nil {
|
||||
that.Display(4, "找不到成品记录,无法进行抽检")
|
||||
return
|
||||
}
|
||||
//判断是否已经抽检了
|
||||
alreadyCheck := that.Db.Get("product_spot_check", "id", Map{"produce_product_id": produceProductId})
|
||||
|
||||
if alreadyCheck == nil {
|
||||
|
||||
that.Db.Update("product", Map{"spot_check_count[#]": "spot_check_count+1"},
|
||||
Map{"id": produceProduct.GetCeilInt("product_id")})
|
||||
|
||||
that.Db.Update("produce", Map{"spot_check_count[#]": "spot_check_count+1"},
|
||||
Map{"id": produceProduct.GetCeilInt("produce_id")})
|
||||
}
|
||||
|
||||
data := Map{
|
||||
"sn": sn,
|
||||
"rule": rule,
|
||||
"admin_id": adminID,
|
||||
"create_time": time.Now().Unix(),
|
||||
"modify_time": time.Now().Unix(),
|
||||
"product_id": produceProduct.GetCeilInt("product_id"),
|
||||
"produce_id": produceProduct.GetCeilInt("produce_id"),
|
||||
"produce_product_id": produceProductId,
|
||||
"description": description,
|
||||
"state": state,
|
||||
}
|
||||
|
||||
id := that.Db.Insert("product_spot_check", data)
|
||||
if id == 0 {
|
||||
that.Display(4, "添加抽检记录失败,请重新添加")
|
||||
return
|
||||
}
|
||||
|
||||
data["id"] = id
|
||||
|
||||
that.Display(0, data)
|
||||
},
|
||||
"search": func(that *Context) {
|
||||
|
||||
adminID := that.Session("id").ToInt()
|
||||
|
||||
if adminID == 0 {
|
||||
that.Display(2, "登录失效,请重新登录")
|
||||
return
|
||||
}
|
||||
page := ObjToInt(that.Req.FormValue("page"))
|
||||
pageSize := ObjToInt(that.Req.FormValue("pageSize"))
|
||||
productId := ObjToInt(that.Req.FormValue("id"))
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
columnStr := "product_spot_check.id,product_spot_check.product_id,product_spot_check.sn,product.name,product_spot_check.img,product_spot_check.admin_id,admin.name AS admin_name,product_spot_check.modify_time,product_spot_check.state"
|
||||
leftJoin := Map{"[><]product": "product_spot_check.product_id=product.id",
|
||||
"[><]admin": "product_spot_check.admin_id=admin.id",
|
||||
}
|
||||
|
||||
where := Map{"ORDER": "id DESC"}
|
||||
|
||||
if productId != 0 {
|
||||
where["product_id"] = productId
|
||||
}
|
||||
|
||||
count := that.Db.Count("product_spot_check", where)
|
||||
|
||||
reData := that.Db.Page(page, pageSize).
|
||||
PageSelect("product_spot_check", leftJoin, columnStr, where)
|
||||
|
||||
that.Display(0, Map{"count": count, "data": reData})
|
||||
},
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "../../../hotime"
|
||||
"../../dri/ddsms"
|
||||
)
|
||||
|
||||
var Sms = Ctr{
|
||||
//只允许微信验证过的或者登录成功的发送短信
|
||||
"send": func(this *Context) {
|
||||
//if this.Session("uid").Data == nil && this.Session("wechatInfo").Data == nil {
|
||||
// this.Display(2, "没有授权")
|
||||
// return
|
||||
//}
|
||||
if len(this.Req.FormValue("token")) != 32 {
|
||||
this.Display(2, "没有授权")
|
||||
return
|
||||
}
|
||||
|
||||
phone := this.Req.FormValue("phone")
|
||||
if len(phone) < 11 {
|
||||
this.Display(3, "手机号格式错误")
|
||||
return
|
||||
}
|
||||
code := getCode()
|
||||
this.Session("phone", phone)
|
||||
this.Session("code", code)
|
||||
|
||||
ddsms.DefaultDDY.SendYZM(phone, this.Config.GetString("smsLogin"), map[string]string{"code": code})
|
||||
|
||||
this.Display(0, "发送成功")
|
||||
},
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+14
-15
@@ -2,7 +2,8 @@
|
||||
"cache": {
|
||||
"db": {
|
||||
"db": false,
|
||||
"session": true
|
||||
"session": true,
|
||||
"timeout": 7200
|
||||
},
|
||||
"memory": {
|
||||
"db": true,
|
||||
@@ -10,24 +11,22 @@
|
||||
"timeout": 7200
|
||||
}
|
||||
},
|
||||
"codeConfig": {
|
||||
"admin": "config/app.json"
|
||||
},
|
||||
"codeConfig1": {
|
||||
"admin": {
|
||||
"config": "config/app.json",
|
||||
"package": "admin",
|
||||
"rule": "config/rule.json"
|
||||
"codeConfig": [
|
||||
{
|
||||
"config": "config/admin.json",
|
||||
"configDB": "config/adminDB.json",
|
||||
"mode": 0,
|
||||
"name": "",
|
||||
"rule": "config/rule.json",
|
||||
"table": "admin"
|
||||
}
|
||||
},
|
||||
"crossDomain": "auto",
|
||||
],
|
||||
"db": {
|
||||
"mysql": {
|
||||
"host": "192.168.6.253",
|
||||
"name": "myhs",
|
||||
"name": "dgs-cms",
|
||||
"password": "dasda8454456",
|
||||
"port": "3306",
|
||||
"prefix": "",
|
||||
"user": "root"
|
||||
}
|
||||
},
|
||||
@@ -42,8 +41,8 @@
|
||||
"4": "数据处理异常",
|
||||
"5": "数据结果异常"
|
||||
},
|
||||
"mode": 3,
|
||||
"port": "8080",
|
||||
"mode": 2,
|
||||
"port": "8081",
|
||||
"sessionName": "HOTIME",
|
||||
"tpt": "tpt"
|
||||
}
|
||||
@@ -20,10 +20,17 @@
|
||||
},
|
||||
"注释": "可配置memory,db,redis,默认启用memory,默认优先级为memory\u003eredis\u003edb,memory与数据库缓存设置项一致,缓存数据填充会自动反方向反哺,加入memory缓存过期将自动从redis更新,但memory永远不会更新redis,如果是集群建议不要开启memory,配置即启用"
|
||||
},
|
||||
"codeConfig": {
|
||||
"packageName": "默认无,必须,包名称以及应用名,生成代码的配置文件地址,比如config/app.json,数据库有更新时自动更新配置文件以及对应的生成文件",
|
||||
"注释": "配置即启用,非必须,默认无"
|
||||
},
|
||||
"codeConfig": [
|
||||
"注释:配置即启用,非必须,默认无",
|
||||
{
|
||||
"config": "默认config/app.json,必须,接口描述配置文件",
|
||||
"configDB": "默认无,非必须,有则每次将数据库数据生成到此目录用于配置读写,无则不生成",
|
||||
"mode": "默认0,非必须,0为内嵌代码模式,1为生成代码模式",
|
||||
"name": "默认无,非必须,有则生成代码到此目录,无则采用缺省模式使用表名,如设置为:admin,将在admin目录生成包名为admin的代码",
|
||||
"rule": "默认config/rule.json,非必须,有则按改规则生成接口,无则按系统内嵌方式生成",
|
||||
"table": "默认admin,必须,根据数据库内当前表名做为用户生成数据"
|
||||
}
|
||||
],
|
||||
"crossDomain": "默认空 非必须,空字符串为不开启,如果需要跨域设置,auto为智能开启所有网站允许跨域,http://www.baidu.com为指定域允许跨域",
|
||||
"db": {
|
||||
"mysql": {
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,422 @@
|
||||
[
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "idcard",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "id",
|
||||
"strict": true,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "sn",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": false,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "parent_ids",
|
||||
"strict": true,
|
||||
"type": "index"
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": false,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "index",
|
||||
"strict": true,
|
||||
"type": "index"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "parent_id",
|
||||
"true": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "amount",
|
||||
"strict": true,
|
||||
"type": "money"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "info",
|
||||
"strict": false,
|
||||
"type": "textArea"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "status",
|
||||
"strict": false,
|
||||
"type": "select"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "state",
|
||||
"strict": false,
|
||||
"type": "select"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "sex",
|
||||
"strict": false,
|
||||
"type": "select"
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": false,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "delete",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "lat",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "lng",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "latitude",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "longitude",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": false,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "password",
|
||||
"strict": false,
|
||||
"type": "password"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": false,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "pwd",
|
||||
"strict": false,
|
||||
"type": "password"
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": false,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "version",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "seq",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "sort",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "note",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "description",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "abstract",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "content",
|
||||
"strict": false,
|
||||
"type": "textArea"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "address",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "full_name",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "create_time",
|
||||
"strict": true,
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "modify_time",
|
||||
"strict": true,
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "image",
|
||||
"strict": false,
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "img",
|
||||
"strict": false,
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "avatar",
|
||||
"strict": false,
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "icon",
|
||||
"strict": false,
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "file",
|
||||
"strict": false,
|
||||
"type": "file"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "age",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "email",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "time",
|
||||
"strict": false,
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "level",
|
||||
"strict": false,
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "rule",
|
||||
"strict": false,
|
||||
"type": "form"
|
||||
},
|
||||
{
|
||||
"add": true,
|
||||
"edit": true,
|
||||
"info": true,
|
||||
"list": false,
|
||||
"must": false,
|
||||
"name": "auth",
|
||||
"strict": false,
|
||||
"type": "auth"
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "table",
|
||||
"strict": false,
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"add": false,
|
||||
"edit": false,
|
||||
"info": true,
|
||||
"list": true,
|
||||
"must": false,
|
||||
"name": "table_id",
|
||||
"strict": false,
|
||||
"type": "table_id"
|
||||
}
|
||||
]
|
||||
+6
-20
@@ -1,31 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"../../hotime"
|
||||
"../dri/ddsms"
|
||||
"./app"
|
||||
"fmt"
|
||||
"time"
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
date, _ := time.Parse("2006-01-02 15:04", time.Now().Format("2006-01-02")+" 14:00")
|
||||
fmt.Println(date, date.Unix())
|
||||
//fmt.Println("0123456"[1:7])
|
||||
appIns := hotime.Init("config/config.json")
|
||||
//RESTfull接口适配
|
||||
appIns.SetConnectListener(func(context *hotime.Context) bool {
|
||||
|
||||
return true
|
||||
appIns := Init("config/config.json")
|
||||
appIns.SetConnectListener(func(that *Context) (isFinished bool) {
|
||||
|
||||
return isFinished
|
||||
})
|
||||
|
||||
//makeCode := code.MakeCode{}
|
||||
//fmt.Println(common.ObjToStr(makeCode.Db2JSON("admin","test",appIns.Db)))
|
||||
if ddsms.DefaultDDY.ApiKey == "" {
|
||||
ddsms.DefaultDDY.Init(appIns.Config.GetString("smsKey"))
|
||||
}
|
||||
appIns.Run(Router{})
|
||||
|
||||
appIns.Run(hotime.Router{
|
||||
"app": app.Project,
|
||||
})
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.left-nav-home-bar{background:#2c3759!important}.left-nav-home-bar,.left-nav-home-bar i{color:#fff!important}.el-submenu .el-menu-item{height:40px;line-height:40px}.el-menu .el-submenu__title{height:46px;line-height:46px}.left-nav-home-bar i{margin-bottom:6px!important}.el-menu-item-group__title{padding:0 0 0 20px}.head-left[data-v-297b6686],.head-right[data-v-297b6686]{display:flex;justify-content:center;flex-direction:column}.head-right[data-v-297b6686]{align-items:flex-end}.el-upload{height:100px;width:100px}.el-upload img[data-v-012633a9]{height:100%;width:100%;-o-object-fit:cover;object-fit:cover;display:block}.el-upload i[data-v-012633a9]{font-size:40px;margin:30% 31%;display:block}.form-file-item .upload-file .el-upload{width:100px;height:auto;line-height:0;background-color:transparent}.form-file-item .el-button--small{height:32px}.el-input-number--mini{width:100px;margin-right:5px}.el-input-number--mini .el-input{width:100px!important}.basic-form{width:400px}.basic-form .el-input,.basic-form .el-select{width:84px;margin-right:5px}.form-file-item{margin-bottom:5px}.form-file-item .el-upload{width:60px;height:60px;line-height:60px}.form-file-item .file-item .name{width:100px}.form-file-item .name{width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.form-list .form-common-item{display:flex;margin-bottom:5px}.form-common-item .name,.form-common-item .value{width:88px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.select-list .select-list-item{display:flex;margin-bottom:5px}.select-list-item .name{width:76px}.select-list-item .name,.select-list-item .value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.select-list-item .value{width:77px}.select-wrap{text-align:left;margin-bottom:5px}.select-wrap .select-add-name{display:inline-block;width:87px;text-align:right}.select-wrap .el-input{width:84px}.el-input__inner{padding-left:10px;padding-right:5px}.el-upload{height:60px;width:60px;background:#eee;overflow:hidden}
|
||||
@@ -1 +0,0 @@
|
||||
.not-show-tab-label .el-tabs__header,.not-show-tab-search{display:none}
|
||||
@@ -1 +0,0 @@
|
||||
.el-upload{height:100px;width:100px}.el-upload img[data-v-012633a9]{height:100%;width:100%;-o-object-fit:cover;object-fit:cover;display:block}.el-upload i[data-v-012633a9]{font-size:40px;margin:30% 31%;display:block}.form-file-item .upload-file .el-upload{width:100px;height:auto;line-height:0;background-color:transparent}.form-file-item .el-button--small{height:32px}.el-input-number--mini{width:100px;margin-right:5px}.el-input-number--mini .el-input{width:100px!important}.basic-form{width:400px}.basic-form .el-input,.basic-form .el-select{width:84px;margin-right:5px}.form-file-item{margin-bottom:5px}.form-file-item .el-upload{width:60px;height:60px;line-height:60px}.form-file-item .file-item .name{width:100px}.form-file-item .name{width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.form-list .form-common-item{display:flex;margin-bottom:5px}.form-common-item .name,.form-common-item .value{width:88px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.select-list .select-list-item{display:flex;margin-bottom:5px}.select-list-item .name{width:76px}.select-list-item .name,.select-list-item .value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.select-list-item .value{width:77px}.select-wrap{text-align:left;margin-bottom:5px}.select-wrap .select-add-name{display:inline-block;width:87px;text-align:right}.select-wrap .el-input{width:84px}.el-input__inner{padding-left:10px;padding-right:5px}.el-upload{height:60px;width:60px;background:#eee;overflow:hidden}
|
||||
@@ -1 +0,0 @@
|
||||
body[data-v-581864d3],dd[data-v-581864d3],dl[data-v-581864d3],form[data-v-581864d3],h1[data-v-581864d3],h2[data-v-581864d3],h3[data-v-581864d3],h4[data-v-581864d3],h5[data-v-581864d3],h6[data-v-581864d3],html[data-v-581864d3],ol[data-v-581864d3],p[data-v-581864d3],pre[data-v-581864d3],tbody[data-v-581864d3],textarea[data-v-581864d3],tfoot[data-v-581864d3],thead[data-v-581864d3],ul[data-v-581864d3]{margin:0;font-size:14px;font-family:Microsoft YaHei}dl[data-v-581864d3],ol[data-v-581864d3],ul[data-v-581864d3]{padding:0}li[data-v-581864d3]{list-style:none}input[data-v-581864d3]{border:none;outline:none;font-family:Microsoft YaHei;background-color:#fff}a[data-v-581864d3]{font-family:Microsoft YaHei;text-decoration:none}[data-v-581864d3]{margin:0;padding:0}.login[data-v-581864d3]{position:relative;width:100%;height:100%;background-color:#353d56}.login-item[data-v-581864d3]{position:absolute;top:calc(50% - 244px);left:calc(50% - 244px);width:488px;height:488px;padding:80px 0 152px 0;box-sizing:border-box;background-size:468px 468px}.login-item .left-title[data-v-581864d3]{display:inline-block;width:236px;height:100%;border-right:2px solid #88919e;text-align:center;padding-top:68px;box-sizing:border-box}.login-item .left-title p[data-v-581864d3]{font-size:30px;line-height:50px;color:#fff}.login-item .right-content[data-v-581864d3]{display:inline-block;vertical-align:top;padding-left:42px;box-sizing:border-box}.login-item .right-content .login-title[data-v-581864d3]{font-size:16px;color:#fff}.errorMsg[data-v-581864d3]{width:100%;height:34px;line-height:34px;color:red;font-size:14px;overflow:hidden}.login-item .right-content .inputWrap[data-v-581864d3]{width:204px;height:36px;line-height:36px;color:#99a3b2;font-size:14px;border-bottom:3px solid #98a2b1;margin-bottom:10px}.login-item .right-content .inputWrap input[data-v-581864d3]{background-color:transparent;color:#99a3b2}.login-btn[data-v-581864d3]{width:88px;height:36px;text-align:center;line-height:36px;font-size:14px;color:#fff;background-color:#4f619b;border-radius:18px;margin-top:60px;cursor:pointer}
|
||||
@@ -1 +0,0 @@
|
||||
.form-file-item .upload-file .el-upload{width:100px;height:auto;line-height:0;background-color:transparent}.form-file-item .el-button--small{height:32px}.el-input-number--mini{width:100px;margin-right:5px}.el-input-number--mini .el-input{width:100px!important}.basic-form{width:400px}.basic-form .el-input,.basic-form .el-select{width:84px;margin-right:5px}.form-file-item{margin-bottom:5px}.form-file-item .el-upload{width:60px;height:60px;line-height:60px}.form-file-item .file-item .name{width:100px}.form-file-item .name{width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.form-list .form-common-item{display:flex;margin-bottom:5px}.form-common-item .name,.form-common-item .value{width:88px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.select-list .select-list-item{display:flex;margin-bottom:5px}.select-list-item .name{width:76px}.select-list-item .name,.select-list-item .value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.select-list-item .value{width:77px}.select-wrap{text-align:left;margin-bottom:5px}.select-wrap .select-add-name{display:inline-block;width:87px;text-align:right}.select-wrap .el-input{width:84px}.el-input__inner{padding-left:10px;padding-right:5px}.el-upload,.el-upload-list--picture-card .el-upload-list__item{height:60px;width:60px}.el-upload-list__item.is-success.focusing .el-icon-close-tip{display:none!important}.el-upload{height:100px;width:100px;background:#eee;overflow:hidden}.file-upload .el-upload{background:transparent;width:100%;height:auto;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.file-upload .el-upload .el-button{margin-right:10px}.el-upload img[data-v-4b440c12]{height:100%;width:100%;-o-object-fit:cover;object-fit:cover;display:block}.el-upload i[data-v-4b440c12]{font-size:40px;margin:30% 31%;display:block}
|
||||
@@ -1 +0,0 @@
|
||||
h3[data-v-28af1b93]{margin:40px 0 0}ul[data-v-28af1b93]{list-style-type:none;padding:0}li[data-v-28af1b93]{display:inline-block;margin:0 10px}a[data-v-28af1b93]{color:#42b983}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 297 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 120 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
Binary file not shown.
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title></title><style>body{
|
||||
margin: 0px;
|
||||
}</style><link href="css/chunk-2b8aef56.7087d841.css" rel="prefetch"><link href="css/chunk-38db7d04.2b6ce0ac.css" rel="prefetch"><link href="css/chunk-5c99f384.31e35517.css" rel="prefetch"><link href="css/chunk-60f282ff.83752cba.css" rel="prefetch"><link href="css/chunk-a74869b6.c460e209.css" rel="prefetch"><link href="css/chunk-d1a9ebe6.5cc24c46.css" rel="prefetch"><link href="js/chunk-28c289a1.0ed6fefe.js" rel="prefetch"><link href="js/chunk-2b8aef56.8330998b.js" rel="prefetch"><link href="js/chunk-2c065dd6.d9c3e429.js" rel="prefetch"><link href="js/chunk-38db7d04.18ee879a.js" rel="prefetch"><link href="js/chunk-58db4e7f.c298e695.js" rel="prefetch"><link href="js/chunk-5c99f384.be52d852.js" rel="prefetch"><link href="js/chunk-60f282ff.cbb91cc0.js" rel="prefetch"><link href="js/chunk-78ba61e2.520b239c.js" rel="prefetch"><link href="js/chunk-a74869b6.01e5db7b.js" rel="prefetch"><link href="js/chunk-d1a9ebe6.fba0f501.js" rel="prefetch"><link href="css/app.5e2eb449.css" rel="preload" as="style"><link href="js/app.c87636c4.js" rel="preload" as="script"><link href="css/app.5e2eb449.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but hotime doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/app.c87636c4.js"></script></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-2c065dd6"],{1:function(t,e){},2934:function(t,e,n){"use strict";n.d(e,"e",function(){return o}),n.d(e,"d",function(){return a}),n.d(e,"b",function(){return s}),n.d(e,"c",function(){return u}),n.d(e,"a",function(){return c}),n.d(e,"f",function(){return d});var e=n("b0c0"),r=n("b775"),e=n("4328"),i=n.n(e);function o(t,e){return Object(r.b)({url:t,method:"GET",params:e})}function a(t,e){return Object(r.b)({url:t,method:"GET",params:e})}function s(t,e){return Object(r.b)({url:t,method:"DELETE",data:i.a.stringify(e)})}function u(t,e){return Object(r.b)({url:t,method:"PUT",data:i.a.stringify(e)})}function c(t,e){return Object(r.b)({url:t,method:"POST",data:i.a.stringify(e)})}function d(t){var e=new FormData;return e.append("file",t,t.name),Object(r.b)({url:"file",headers:{"Content-Type":"multipart/form-data"},method:"POST",data:e})}},"83c5":function(t,e,n){"use strict";n("159b");e.a={list:{},constructor:function(){this.list={}},$on:function(t,e){this.list[t]=this.list[t]||[],this.list[t].push(e)},$emit:function(t,e){this.list[t]&&this.list[t].forEach(function(t){t(e)})},$off:function(t){this.list[t]&&delete this.list[t]}}},b775:function(t,e,n){"use strict";n.d(e,"a",function(){return u});var r=n("d3b7"),i=n("bc3a"),r=n.n(i),i=n("4328"),o=n.n(i),a=n("2bea"),s=n("56d7"),u="";r.a.defaults.withCredentials=!0,r.a.defaults.headers.post["Content-Type"]="application/x-www-form-urlencoded;charset=UTF-8";r=r.a.create({baseURL:u,timeout:2e4});r.interceptors.request.use(function(t){return"get"===t.method&&(t.paramsSerializer=function(t){return o.a.stringify(t,{arrayFormat:"repeat"})}),t.withCredentials=!0,t},function(t){Promise.reject(t)}),r.interceptors.response.use(function(t){return 2!==t.data.statu?t.data:(a.b.Message({showClose:!0,message:"登录信息丢失,请重新登陆!",type:"error",duration:2e3,offset:100}),void s.app.$router.push({path:"/login"}))},function(t){var e="err:"+t,n={};return t.response?n=t.response.data:(-1!=e.indexOf("timeout")?(n.code=500,n.message="请求超时,请稍后再试"):-1!=e.indexOf("Network Error")?(n.code=500,n.message="网络错误,请稍后再试"):(n.code=500,n.message=""+t),n)}),e.b=r}}]);
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-58db4e7f"],{"159b":function(c,n,o){var r,t=o("da84"),a=o("fdbc"),f=o("17c2"),i=o("9112");for(r in a){var u=t[r],u=u&&u.prototype;if(u&&u.forEach!==f)try{i(u,"forEach",f)}catch(c){u.forEach=f}}},"17c2":function(c,n,o){"use strict";var r=o("b727").forEach,o=o("a640")("forEach");c.exports=o?[].forEach:function(c){return r(this,c,1<arguments.length?arguments[1]:void 0)}},a640:function(c,n,o){"use strict";var r=o("d039");c.exports=function(c,n){var o=[][c];return!!o&&r(function(){o.call(null,n||function(){throw 1},1)})}}}]);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-a74869b6"],{1:function(t,e){},2934:function(t,e,n){"use strict";n.d(e,"e",function(){return a}),n.d(e,"d",function(){return s}),n.d(e,"b",function(){return i}),n.d(e,"c",function(){return c}),n.d(e,"a",function(){return u}),n.d(e,"f",function(){return d});var e=n("b0c0"),o=n("b775"),e=n("4328"),r=n.n(e);function a(t,e){return Object(o.b)({url:t,method:"GET",params:e})}function s(t,e){return Object(o.b)({url:t,method:"GET",params:e})}function i(t,e){return Object(o.b)({url:t,method:"DELETE",data:r.a.stringify(e)})}function c(t,e){return Object(o.b)({url:t,method:"PUT",data:r.a.stringify(e)})}function u(t,e){return Object(o.b)({url:t,method:"POST",data:r.a.stringify(e)})}function d(t){var e=new FormData;return e.append("file",t,t.name),Object(o.b)({url:"file",headers:{"Content-Type":"multipart/form-data"},method:"POST",data:e})}},"40a7":function(t,e,n){},"578a":function(t,e,n){"use strict";n.r(e);n("b0c0");var s=n("9f9f");Object(s.O)("data-v-581864d3");var i={class:"login",style:{width:"100%",height:"100vh"}},c={class:"login-item"},u={class:"left-title"},d={class:"right-content"},l=Object(s.o)("p",{class:"login-title"},"登录",-1),f={style:{height:"40px"}},b={class:"inputWrap"},p=Object(s.r)(" 账号:"),m={class:"inputWrap"},h=Object(s.r)(" 密码:");Object(s.M)();var o=n("2934"),r={name:"Login",data:function(){return{showLog:!1,showLogInfo:"",label:"HoTime DashBoard",form:{name:"",password:""}}},methods:{login:function(){var e=this;if(""==this.name||""==this.password)return this.showLogInfo="参数不足!",void(this.showLog=!0);Object(o.a)(window.Hotime.data.name+"/hotime/login",e.form).then(function(t){return 0!=t.status?(e.showLogInfo=t.error.msg,void(e.showLog=!0)):void e.$router.push({path:"/"})})}},mounted:function(){this.label=window.Hotime.data.label}};n("8d39");r.render=function(t,e,n,o,r,a){return Object(s.L)(),Object(s.n)("div",i,[Object(s.o)("div",c,[Object(s.o)("div",u,[Object(s.o)("p",null,Object(s.Y)(r.label),1)]),Object(s.o)("div",d,[l,Object(s.o)("div",f,[Object(s.kb)(Object(s.o)("p",{class:"errorMsg"},Object(s.Y)(r.showLogInfo),513),[[s.gb,r.showLog]])]),Object(s.o)("p",b,[p,Object(s.kb)(Object(s.o)("input",{type:"text","onUpdate:modelValue":e[0]||(e[0]=function(t){return r.form.name=t}),class:"accountVal"},null,512),[[s.fb,r.form.name]])]),Object(s.o)("p",m,[h,Object(s.kb)(Object(s.o)("input",{type:"password","onUpdate:modelValue":e[1]||(e[1]=function(t){return r.form.password=t}),class:"passwordVal"},null,512),[[s.fb,r.form.password]])]),Object(s.o)("p",{class:"login-btn",onClick:e[2]||(e[2]=function(){return a.login&&a.login.apply(a,arguments)})},"登录")])])])},r.__scopeId="data-v-581864d3";e.default=r},"8d39":function(t,e,n){"use strict";n("40a7")},b775:function(t,e,n){"use strict";n.d(e,"a",function(){return c});var o=n("d3b7"),r=n("bc3a"),o=n.n(r),r=n("4328"),a=n.n(r),s=n("2bea"),i=n("56d7"),c="";o.a.defaults.withCredentials=!0,o.a.defaults.headers.post["Content-Type"]="application/x-www-form-urlencoded;charset=UTF-8";o=o.a.create({baseURL:c,timeout:2e4});o.interceptors.request.use(function(t){return"get"===t.method&&(t.paramsSerializer=function(t){return a.a.stringify(t,{arrayFormat:"repeat"})}),t.withCredentials=!0,t},function(t){Promise.reject(t)}),o.interceptors.response.use(function(t){return 2!==t.data.statu?t.data:(s.b.Message({showClose:!0,message:"登录信息丢失,请重新登陆!",type:"error",duration:2e3,offset:100}),void i.app.$router.push({path:"/login"}))},function(t){var e="err:"+t,n={};return t.response?n=t.response.data:(-1!=e.indexOf("timeout")?(n.code=500,n.message="请求超时,请稍后再试"):-1!=e.indexOf("Network Error")?(n.code=500,n.message="网络错误,请稍后再试"):(n.code=500,n.message=""+t),n)}),e.b=o}}]);
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-d1a9ebe6"],{"587e":function(a,e,t){"use strict";t.r(e);var n=t("9f9f");Object(n.O)("data-v-28af1b93");var v={class:"hello"},b=Object(n.q)('<p data-v-28af1b93> For a guide and recipes on how to configure / customize this project,<br data-v-28af1b93> check out the <a href="https://cli.vuejs.org" target="_blank" rel="noopener" data-v-28af1b93>vue-cli documentation</a>. </p><h3 data-v-28af1b93>Installed CLI Plugins</h3><ul data-v-28af1b93><li data-v-28af1b93><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener" data-v-28af1b93>babel</a></li><li data-v-28af1b93><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener" data-v-28af1b93>eslint</a></li></ul><h3 data-v-28af1b93>Essential Links</h3><ul data-v-28af1b93><li data-v-28af1b93><a href="https://vuejs.org" target="_blank" rel="noopener" data-v-28af1b93>Core Docs</a></li><li data-v-28af1b93><a href="https://forum.vuejs.org" target="_blank" rel="noopener" data-v-28af1b93>Forum</a></li><li data-v-28af1b93><a href="https://chat.vuejs.org" target="_blank" rel="noopener" data-v-28af1b93>Community Chat</a></li><li data-v-28af1b93><a href="https://twitter.com/vuejs" target="_blank" rel="noopener" data-v-28af1b93>Twitter</a></li><li data-v-28af1b93><a href="https://news.vuejs.org" target="_blank" rel="noopener" data-v-28af1b93>News</a></li></ul><h3 data-v-28af1b93>Ecosystem</h3><ul data-v-28af1b93><li data-v-28af1b93><a href="https://router.vuejs.org" target="_blank" rel="noopener" data-v-28af1b93>vue-router</a></li><li data-v-28af1b93><a href="https://vuex.vuejs.org" target="_blank" rel="noopener" data-v-28af1b93>vuex</a></li><li data-v-28af1b93><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener" data-v-28af1b93>vue-devtools</a></li><li data-v-28af1b93><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener" data-v-28af1b93>vue-loader</a></li><li data-v-28af1b93><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener" data-v-28af1b93>awesome-vue</a></li></ul>',7);Object(n.M)();var r={name:"HelloWorld",props:{msg:String}};t("6fe9");r.render=function(a,e,t,r,l,o){return Object(n.L)(),Object(n.n)("div",v,[Object(n.o)("h1",null,Object(n.Y)(t.msg),1),b])},r.__scopeId="data-v-28af1b93";e.default=r},"6fe9":function(a,e,t){"use strict";t("e8a7")},e8a7:function(a,e,t){}}]);
|
||||
@@ -0,0 +1,17 @@
|
||||
module code.hoteas.com/golang/hotime
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/360EntSecGroup-Skylar/excelize v1.4.1
|
||||
github.com/garyburd/redigo v1.6.3
|
||||
github.com/go-pay/gopay v1.5.78
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/mattn/go-sqlite3 v1.14.12
|
||||
github.com/silenceper/wechat/v2 v2.1.2
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.364
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ocr v1.0.364
|
||||
go.mongodb.org/mongo-driver v1.10.1
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
)
|
||||
@@ -0,0 +1,107 @@
|
||||
github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=
|
||||
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/garyburd/redigo v1.6.3 h1:HCeeRluvAgMusMomi1+6Y5dmFOdYV/JzoRrrbFlkGIc=
|
||||
github.com/garyburd/redigo v1.6.3/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw=
|
||||
github.com/go-pay/gopay v1.5.78 h1:wIHp8g/jK0ik5bZo2MWt3jAQsktT3nkdXZxlRZvljko=
|
||||
github.com/go-pay/gopay v1.5.78/go.mod h1:M6Nlk2VdZHCbWphOw3rtbnz4SiOk6Xvxg6mxwDfg+Ps=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
|
||||
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/silenceper/wechat/v2 v2.1.2 h1:+QfIMiYfwST2ZloTwmYp0O0p5Y1LYRZxfLWfMuSE30k=
|
||||
github.com/silenceper/wechat/v2 v2.1.2/go.mod h1:0OprxYCCp2CZAKw06BBlnaczInTk2KxOLsKeiopshGg=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.364 h1:X1Jws4XqrTH+p7FBQ7BpjW4qFXObKHWm0/XhW/GvqRs=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.364/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ocr v1.0.364 h1:kbor60vo37v7Hu+i17gooox9Rw281fVHNna8zwtDG1w=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ocr v1.0.364/go.mod h1:LeIUBOLhc+Y5YCEpZrULPD9lgoXXV4/EmIcoEvmHz9c=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
|
||||
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
go.mongodb.org/mongo-driver v1.10.1 h1:NujsPveKwHaWuKUer/ceo9DzEe7HIj1SlJ6uvXZG0S4=
|
||||
go.mongodb.org/mongo-driver v1.10.1/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
+22
-3
@@ -82,10 +82,29 @@ func findCaller(skip int) string {
|
||||
for i := 0; i < 10; i++ {
|
||||
file, line = getCaller(skip + i)
|
||||
if !strings.HasPrefix(file, "logrus") {
|
||||
|
||||
if file == "common/error.go" {
|
||||
file, line = getCaller(skip + i + 1)
|
||||
j := 0
|
||||
for true {
|
||||
j++
|
||||
if file == "common/error.go" {
|
||||
file, line = getCaller(skip + i + j)
|
||||
}
|
||||
if file == "db/hotimedb.go" {
|
||||
file, line = getCaller(skip + i + j)
|
||||
}
|
||||
if file == "code/makecode.go" {
|
||||
file, line = getCaller(skip + i + j)
|
||||
}
|
||||
if strings.Index(file, "common/") == 0 {
|
||||
file, line = getCaller(skip + i + j)
|
||||
}
|
||||
if strings.Contains(file, "application.go") {
|
||||
file, line = getCaller(skip + i + j)
|
||||
}
|
||||
if j == 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -44,17 +44,17 @@ func Run() {
|
||||
appIns.Run(hotime.Router{
|
||||
"app": hotime.Proj{
|
||||
"index": hotime.Ctr{
|
||||
"test": func(this *hotime.Context) {
|
||||
fmt.Println(this.Db.GetTag())
|
||||
//x := this.Db.Action(func(db hotime.HoTimeDB) bool {
|
||||
"test": func(that *hotime.Context) {
|
||||
fmt.Println(that.Db.GetTag())
|
||||
//x := that.Db.Action(func(db hotime.HoTimeDB) bool {
|
||||
//
|
||||
// db.Insert("user", hotime.Map{"unickname": "dasdas"})
|
||||
//
|
||||
// return true
|
||||
//})
|
||||
this.Display(0, 1)
|
||||
that.Display(0, 1)
|
||||
},
|
||||
"websocket": func(this *hotime.Context) {
|
||||
"websocket": func(that *hotime.Context) {
|
||||
hdler := websocket.Handler(func(ws *websocket.Conn) {
|
||||
for true {
|
||||
msg := make([]byte, 5120)
|
||||
@@ -73,7 +73,7 @@ func Run() {
|
||||
fmt.Printf("Send: %s\n", msg[:m])
|
||||
}
|
||||
})
|
||||
hdler.ServeHTTP(this.Resp, this.Req)
|
||||
hdler.ServeHTTP(that.Resp, that.Req)
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
package hotime
|
||||
|
||||
import (
|
||||
. "./cache"
|
||||
. "./common"
|
||||
. "code.hoteas.com/golang/hotime/cache"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
)
|
||||
|
||||
//session对象
|
||||
|
||||
@@ -5,4 +5,4 @@ type Ctr map[string]Method
|
||||
type Proj map[string]Ctr
|
||||
type Router map[string]Proj
|
||||
type MethodRouter map[string]Method //直接字符串关联函数
|
||||
type Method func(this *Context)
|
||||
type Method func(that *Context)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package hotime
|
||||
|
||||
import (
|
||||
. "./common"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
)
|
||||
|
||||
var IsRun = false //当前状态
|
||||
@@ -10,15 +10,17 @@ var App = map[string]*Application{} //整个项目
|
||||
//var Db = HoTimeDB{} //数据库实例
|
||||
|
||||
var Config = Map{
|
||||
"mode": 3, //模式 0生产模式,1,测试模式,2,开发模式,3,内嵌代码模式
|
||||
"codeConfig": Map{
|
||||
"admin": "config/app.json",
|
||||
},
|
||||
"codeConfig1": Map{
|
||||
"admin": Map{ //默认无,必须,接口类别名称
|
||||
"package": "admin", //默认admin,非必须,有则生成代码到此目录,无则采用缺省模式不生成代码只生成接口
|
||||
"config": "config/app.json", //默认config/app.json,必须,接口描述配置文件
|
||||
"rule": "config/rule.json", //默认config/rule.json,非必须,有则按改规则生成接口,无则按系统内嵌方式生成
|
||||
"mode": 2, //模式 0生产模式,1,测试模式,2,开发模式
|
||||
//"codeConfig": Map{
|
||||
// "admin": "config/app.json",
|
||||
//},
|
||||
"codeConfig": Slice{
|
||||
Map{
|
||||
"table": "admin", //默认admin,必须,有则根据数据库内当前表名做为用户生成数据
|
||||
"name": "", //默认admin,非必须,有则生成代码到此目录,无则采用缺省模式使用表名
|
||||
"config": "config/app.json", //默认config/app.json,必须,接口描述配置文件,无则自动生成
|
||||
"rule": "config/rule.json", //默认config/rule.json,非必须,有则按改规则生成接口,无则按系统内嵌方式生成
|
||||
"mode": 0, //默认0,非必须,0为内嵌代码模式,1为生成代码模式
|
||||
},
|
||||
},
|
||||
"db": Map{
|
||||
@@ -52,12 +54,24 @@ var ConfigNote = Map{
|
||||
"webConnectLogShow": "默认true,非必须,访问日志如果需要web访问链接、访问ip、访问时间打印,false为关闭true开启此功能",
|
||||
"webConnectLogFile": "无默认,非必须,webConnectLogShow开启之后才能使用,如果需要存储日志文件时使用,保存格式为:a/b/c/20060102150405.txt,将生成:a/b/c/年月日时分秒.txt,按需设置",
|
||||
"mode": "默认0,非必须,0生产模式,1,测试模式,2开发模式,3内嵌代码模式,在开发模式下会显示更多的数据用于开发测试,并能够辅助研发,自动生成配置文件、代码等功能,web无缓存,数据库不启用缓存", //debug 0关闭1开启
|
||||
"codeConfig": Map{
|
||||
"注释": "配置即启用,非必须,默认无",
|
||||
//"package":"默认admin,必须,mode模式为2时会自动生成包文件夹和代码文件",
|
||||
//"path":""
|
||||
|
||||
"packageName": "默认无,必须,包名称以及应用名,生成代码的配置文件地址,比如config/app.json,数据库有更新时自动更新配置文件以及对应的生成文件",
|
||||
//"codeConfig": Map{
|
||||
// "注释": "配置即启用,非必须,默认无",
|
||||
// //"package":"默认admin,必须,mode模式为2时会自动生成包文件夹和代码文件",
|
||||
// //"path":""
|
||||
//
|
||||
// "packageName": "默认无,必须,包名称以及应用名,生成代码的配置文件地址,比如config/app.json,数据库有更新时自动更新配置文件以及对应的生成文件",
|
||||
//},
|
||||
"codeConfig": Slice{
|
||||
"注释:配置即启用,非必须,默认无",
|
||||
Map{ //默认无,必须,接口类别名称
|
||||
//"注释": "", //
|
||||
"table": "默认admin,必须,根据数据库内当前表名做为用户生成数据",
|
||||
"name": "默认无,非必须,有则生成代码到此目录,无则采用缺省模式使用表名,如设置为:admin,将在admin目录生成包名为admin的代码",
|
||||
"config": "默认config/app.json,必须,接口描述配置文件", //
|
||||
"configDB": "默认无,非必须,有则每次将数据库数据生成到此目录用于配置读写,无则不生成", //
|
||||
"rule": "默认config/rule.json,非必须,有则按改规则生成接口,无则按系统内嵌方式生成",
|
||||
"mode": "默认0,非必须,0为内嵌代码模式,1为生成代码模式",
|
||||
},
|
||||
},
|
||||
"db": Map{
|
||||
"注释": "配置即启用,非必须,默认使用sqlite数据库",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
test/Test*.xlsx
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
language: go
|
||||
|
||||
install:
|
||||
- go get -d -t -v ./... && go build -v ./...
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- GOARCH=amd64
|
||||
- GOARCH=386
|
||||
|
||||
script:
|
||||
- go vet ./...
|
||||
- go test ./... -v -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [xuri.me](https://xuri.me). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
<!-- use this template to generate the contributor docs with the following command: `$ lingo run docs --template CONTRIBUTING_TEMPLATE.md --output CONTRIBUTING.md` -->
|
||||
# Contributing to excelize
|
||||
|
||||
Want to hack on excelize? Awesome! This page contains information about reporting issues as well as some tips and
|
||||
guidelines useful to experienced open source contributors. Finally, make sure
|
||||
you read our [community guidelines](#community-guidelines) before you
|
||||
start participating.
|
||||
|
||||
## Topics
|
||||
|
||||
* [Reporting Security Issues](#reporting-security-issues)
|
||||
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
|
||||
* [Reporting Issues](#reporting-other-issues)
|
||||
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
|
||||
* [Community Guidelines](#community-guidelines)
|
||||
|
||||
## Reporting security issues
|
||||
|
||||
The excelize maintainers take security seriously. If you discover a security
|
||||
issue, please bring it to their attention right away!
|
||||
|
||||
Please **DO NOT** file a public issue, instead send your report privately to
|
||||
[xuri.me](https://xuri.me).
|
||||
|
||||
Security reports are greatly appreciated and we will publicly thank you for it.
|
||||
We currently do not offer a paid security bounty program, but are not
|
||||
ruling it out in the future.
|
||||
|
||||
## Reporting other issues
|
||||
|
||||
A great way to contribute to the project is to send a detailed report when you
|
||||
encounter an issue. We always appreciate a well-written, thorough bug report,
|
||||
and will thank you for it!
|
||||
|
||||
Check that [our issue database](https://github.com/360EntSecGroup-Skylar/excelize/issues)
|
||||
doesn't already include that problem or suggestion before submitting an issue.
|
||||
If you find a match, you can use the "subscribe" button to get notified on
|
||||
updates. Do *not* leave random "+1" or "I have this too" comments, as they
|
||||
only clutter the discussion, and don't help resolving it. However, if you
|
||||
have ways to reproduce the issue or have additional information that may help
|
||||
resolving the issue, please leave a comment.
|
||||
|
||||
When reporting issues, always include the output of `go env`.
|
||||
|
||||
Also include the steps required to reproduce the problem if possible and
|
||||
applicable. This information will help us review and fix your issue faster.
|
||||
When sending lengthy log-files, consider posting them as a gist [https://gist.github.com](https://gist.github.com).
|
||||
Don't forget to remove sensitive data from your logfiles before posting (you can
|
||||
replace those parts with "REDACTED").
|
||||
|
||||
## Quick contribution tips and guidelines
|
||||
|
||||
This section gives the experienced contributor some tips and guidelines.
|
||||
|
||||
### Pull requests are always welcome
|
||||
|
||||
Not sure if that typo is worth a pull request? Found a bug and know how to fix
|
||||
it? Do it! We will appreciate it. Any significant improvement should be
|
||||
documented as [a GitHub issue](https://github.com/360EntSecGroup-Skylar/excelize/issues) before
|
||||
anybody starts working on it.
|
||||
|
||||
We are always thrilled to receive pull requests. We do our best to process them
|
||||
quickly. If your pull request is not accepted on the first try,
|
||||
don't get discouraged!
|
||||
|
||||
### Design and cleanup proposals
|
||||
|
||||
You can propose new designs for existing excelize features. You can also design
|
||||
entirely new features. We really appreciate contributors who want to refactor or
|
||||
otherwise cleanup our project.
|
||||
|
||||
We try hard to keep excelize lean and focused. Excelize can't do everything for
|
||||
everybody. This means that we might decide against incorporating a new feature.
|
||||
However, there might be a way to implement that feature *on top of* excelize.
|
||||
|
||||
### Conventions
|
||||
|
||||
Fork the repository and make changes on your fork in a feature branch:
|
||||
|
||||
* If it's a bug fix branch, name it XXXX-something where XXXX is the number of
|
||||
the issue.
|
||||
* If it's a feature branch, create an enhancement issue to announce
|
||||
your intentions, and name it XXXX-something where XXXX is the number of the
|
||||
issue.
|
||||
|
||||
Submit unit tests for your changes. Go has a great test framework built in; use
|
||||
it! Take a look at existing tests for inspiration. Run the full test on your branch before
|
||||
submitting a pull request.
|
||||
|
||||
Update the documentation when creating or modifying features. Test your
|
||||
documentation changes for clarity, concision, and correctness, as well as a
|
||||
clean documentation build.
|
||||
|
||||
Write clean code. Universally formatted code promotes ease of writing, reading,
|
||||
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
|
||||
committing your changes. Most editors have plug-ins that do this automatically.
|
||||
|
||||
Pull request descriptions should be as clear as possible and include a reference
|
||||
to all the issues that they address.
|
||||
|
||||
### Successful Changes
|
||||
|
||||
Before contributing large or high impact changes, make the effort to coordinate
|
||||
with the maintainers of the project before submitting a pull request. This
|
||||
prevents you from doing extra work that may or may not be merged.
|
||||
|
||||
Large PRs that are just submitted without any prior communication are unlikely
|
||||
to be successful.
|
||||
|
||||
While pull requests are the methodology for submitting changes to code, changes
|
||||
are much more likely to be accepted if they are accompanied by additional
|
||||
engineering work. While we don't define this explicitly, most of these goals
|
||||
are accomplished through communication of the design goals and subsequent
|
||||
solutions. Often times, it helps to first state the problem before presenting
|
||||
solutions.
|
||||
|
||||
Typically, the best methods of accomplishing this are to submit an issue,
|
||||
stating the problem. This issue can include a problem statement and a
|
||||
checklist with requirements. If solutions are proposed, alternatives should be
|
||||
listed and eliminated. Even if the criteria for elimination of a solution is
|
||||
frivolous, say so.
|
||||
|
||||
Larger changes typically work best with design documents. These are focused on
|
||||
providing context to the design at the time the feature was conceived and can
|
||||
inform future documentation contributions.
|
||||
|
||||
### Commit Messages
|
||||
|
||||
Commit messages must start with a capitalized and short summary
|
||||
written in the imperative, followed by an optional, more detailed explanatory
|
||||
text which is separated from the summary by an empty line.
|
||||
|
||||
Commit messages should follow best practices, including explaining the context
|
||||
of the problem and how it was solved, including in caveats or follow up changes
|
||||
required. They should tell the story of the change and provide readers
|
||||
understanding of what led to it.
|
||||
|
||||
In practice, the best approach to maintaining a nice commit message is to
|
||||
leverage a `git add -p` and `git commit --amend` to formulate a solid
|
||||
changeset. This allows one to piece together a change, as information becomes
|
||||
available.
|
||||
|
||||
If you squash a series of commits, don't just submit that. Re-write the commit
|
||||
message, as if the series of commits was a single stroke of brilliance.
|
||||
|
||||
That said, there is no requirement to have a single commit for a PR, as long as
|
||||
each commit tells the story. For example, if there is a feature that requires a
|
||||
package, it might make sense to have the package in a separate commit then have
|
||||
a subsequent commit that uses it.
|
||||
|
||||
Remember, you're telling part of the story with the commit message. Don't make
|
||||
your chapter weird.
|
||||
|
||||
### Review
|
||||
|
||||
Code review comments may be added to your pull request. Discuss, then make the
|
||||
suggested modifications and push additional commits to your feature branch. Post
|
||||
a comment after pushing. New commits show up in the pull request automatically,
|
||||
but the reviewers are notified only when you comment.
|
||||
|
||||
Pull requests must be cleanly rebased on top of master without multiple branches
|
||||
mixed into the PR.
|
||||
|
||||
**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your
|
||||
feature branch to update your pull request rather than `merge master`.
|
||||
|
||||
Before you make a pull request, squash your commits into logical units of work
|
||||
using `git rebase -i` and `git push -f`. A logical unit of work is a consistent
|
||||
set of patches that should be reviewed together: for example, upgrading the
|
||||
version of a vendored dependency and taking advantage of its now available new
|
||||
feature constitute two separate units of work. Implementing a new function and
|
||||
calling it in another file constitute a single logical unit of work. The very
|
||||
high majority of submissions should have a single commit, so if in doubt: squash
|
||||
down to one.
|
||||
|
||||
After every commit, make sure the test passes. Include documentation
|
||||
changes in the same pull request so that a revert would remove all traces of
|
||||
the feature or fix.
|
||||
|
||||
Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in commits that
|
||||
close an issue. Including references automatically closes the issue on a merge.
|
||||
|
||||
Please see the [Coding Style](#coding-style) for further guidelines.
|
||||
|
||||
### Merge approval
|
||||
|
||||
The excelize maintainers use LGTM (Looks Good To Me) in comments on the code review to
|
||||
indicate acceptance.
|
||||
|
||||
### Sign your work
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the patch. Your
|
||||
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||
it on as an open-source patch. The rules are pretty simple: if you can certify
|
||||
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||
|
||||
```text
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
1 Letterman Drive
|
||||
Suite D4700
|
||||
San Francisco, CA, 94129
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
|
||||
Then you just add a line to every git commit message:
|
||||
|
||||
Signed-off-by: Ri Xu https://xuri.me
|
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
||||
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||
commit automatically with `git commit -s`.
|
||||
|
||||
### How can I become a maintainer
|
||||
|
||||
First, all maintainers have 3 things
|
||||
|
||||
* They share responsibility in the project's success.
|
||||
* They have made a long-term, recurring time investment to improve the project.
|
||||
* They spend that time doing whatever needs to be done, not necessarily what
|
||||
is the most interesting or fun.
|
||||
|
||||
Maintainers are often under-appreciated, because their work is harder to appreciate.
|
||||
It's easy to appreciate a really cool and technically advanced feature. It's harder
|
||||
to appreciate the absence of bugs, the slow but steady improvement in stability,
|
||||
or the reliability of a release process. But those things distinguish a good
|
||||
project from a great one.
|
||||
|
||||
Don't forget: being a maintainer is a time investment. Make sure you
|
||||
will have time to make yourself available. You don't have to be a
|
||||
maintainer to make a difference on the project!
|
||||
|
||||
If you want to become a meintainer, contact [xuri.me](https://xuri.me) and given a introduction of you.
|
||||
|
||||
## Community guidelines
|
||||
|
||||
We want to keep the community awesome, growing and collaborative. We need
|
||||
your help to keep it that way. To help with this we've come up with some general
|
||||
guidelines for the community as a whole:
|
||||
|
||||
* Be nice: Be courteous, respectful and polite to fellow community members:
|
||||
no regional, racial, gender, or other abuse will be tolerated. We like
|
||||
nice people way better than mean ones!
|
||||
|
||||
* Encourage diversity and participation: Make everyone in our community feel
|
||||
welcome, regardless of their background and the extent of their
|
||||
contributions, and do everything possible to encourage participation in
|
||||
our community.
|
||||
|
||||
* Keep it legal: Basically, don't get us in trouble. Share only content that
|
||||
you own, do not share private or sensitive information, and don't break
|
||||
the law.
|
||||
|
||||
* Stay on topic: Make sure that you are posting to the correct channel and
|
||||
avoid off-topic discussions. Remember when you update an issue or respond
|
||||
to an email you are potentially sending to a large number of people. Please
|
||||
consider this before you update. Also remember that nobody likes spam.
|
||||
|
||||
* Don't send email to the maintainers: There's no need to send email to the
|
||||
maintainers to ask them to investigate an issue or to take a look at a
|
||||
pull request. Instead of sending an email, GitHub mentions should be
|
||||
used to ping maintainers to review a pull request, a proposal or an
|
||||
issue.
|
||||
|
||||
### Guideline violations — 3 strikes method
|
||||
|
||||
The point of this section is not to find opportunities to punish people, but we
|
||||
do need a fair way to deal with people who are making our community suck.
|
||||
|
||||
1. First occurrence: We'll give you a friendly, but public reminder that the
|
||||
behavior is inappropriate according to our guidelines.
|
||||
|
||||
2. Second occurrence: We will send you a private message with a warning that
|
||||
any additional violations will result in removal from the community.
|
||||
|
||||
3. Third occurrence: Depending on the violation, we may need to delete or ban
|
||||
your account.
|
||||
|
||||
**Notes:**
|
||||
|
||||
* Obvious spammers are banned on first occurrence. If we don't do this, we'll
|
||||
have spam all over the place.
|
||||
|
||||
* Violations are forgiven after 6 months of good behavior, and we won't hold a
|
||||
grudge.
|
||||
|
||||
* People who commit minor infractions will get some education, rather than
|
||||
hammering them in the 3 strikes process.
|
||||
|
||||
* The rules apply equally to everyone in the community, no matter how much
|
||||
you've contributed.
|
||||
|
||||
* Extreme violations of a threatening, abusive, destructive or illegal nature
|
||||
will be addressed immediately and are not subject to 3 strikes or forgiveness.
|
||||
|
||||
* Contact [xuri.me](https://xuri.me) to report abuse or appeal violations. In the case of
|
||||
appeals, we know that mistakes happen, and we'll work with you to come up with a
|
||||
fair solution if there has been a misunderstanding.
|
||||
|
||||
## Coding Style
|
||||
|
||||
Unless explicitly stated, we follow all coding guidelines from the Go
|
||||
community. While some of these standards may seem arbitrary, they somehow seem
|
||||
to result in a solid, consistent codebase.
|
||||
|
||||
It is possible that the code base does not currently comply with these
|
||||
guidelines. We are not looking for a massive PR that fixes this, since that
|
||||
goes against the spirit of the guidelines. All new contributions should make a
|
||||
best effort to clean up and make the code base better than they left it.
|
||||
Obviously, apply your best judgement. Remember, the goal here is to make the
|
||||
code base easier for humans to navigate and understand. Always keep that in
|
||||
mind when nudging others to comply.
|
||||
|
||||
The rules:
|
||||
|
||||
1. All code should be formatted with `gofmt -s`.
|
||||
2. All code should pass the default levels of
|
||||
[`golint`](https://github.com/golang/lint).
|
||||
3. All code should follow the guidelines covered in [Effective
|
||||
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
|
||||
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||
4. Comment the code. Tell us the why, the history and the context.
|
||||
5. Document _all_ declarations and methods, even private ones. Declare
|
||||
expectations, caveats and anything else that may be important. If a type
|
||||
gets exported, having the comments already there will ensure it's ready.
|
||||
6. Variable name length should be proportional to its context and no longer.
|
||||
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
|
||||
In practice, short methods will have short variable names and globals will
|
||||
have longer names.
|
||||
7. No underscores in package names. If you need a compound name, step back,
|
||||
and re-examine why you need a compound name. If you still think you need a
|
||||
compound name, lose the underscore.
|
||||
8. No utils or helpers packages. If a function is not general enough to
|
||||
warrant its own package, it has not been written generally enough to be a
|
||||
part of a util package. Just leave it unexported and well-documented.
|
||||
9. All tests should run with `go test` and outside tooling should not be
|
||||
required. No, we don't need another unit testing framework. Assertion
|
||||
packages are acceptable if they provide _real_ incremental value.
|
||||
10. Even though we call these "rules" above, they are actually just
|
||||
guidelines. Since you've read all the rules, you now know that.
|
||||
|
||||
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
||||
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
|
||||
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
|
||||
kool-aid is a lot easier than going thirsty.
|
||||
|
||||
## Code Review Comments and Effective Go Guidelines
|
||||
[CodeLingo](https://codelingo.io) automatically checks every pull request against the following guidelines from [Effective Go](https://golang.org/doc/effective_go.html) and [Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||
|
||||
|
||||
### Package Comment
|
||||
Every package should have a package comment, a block comment preceding the package clause.
|
||||
For multi-file packages, the package comment only needs to be present in one file, and any one will do.
|
||||
The package comment should introduce the package and provide information relevant to the package as a
|
||||
whole. It will appear first on the godoc page and should set up the detailed documentation that follows.
|
||||
|
||||
|
||||
### Single Method Interface Name
|
||||
By convention, one-method interfaces are named by the method name plus an -er suffix
|
||||
or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.
|
||||
|
||||
There are a number of such names and it's productive to honor them and the function names they capture.
|
||||
Read, Write, Close, Flush, String and so on have canonical signatures and meanings. To avoid confusion,
|
||||
don't give your method one of those names unless it has the same signature and meaning. Conversely,
|
||||
if your type implements a method with the same meaning as a method on a well-known type, give it the
|
||||
same name and signature; call your string-converter method String not ToString.
|
||||
|
||||
|
||||
### Avoid Annotations in Comments
|
||||
Comments do not need extra formatting such as banners of stars. The generated output
|
||||
may not even be presented in a fixed-width font, so don't depend on spacing for alignment—godoc,
|
||||
like gofmt, takes care of that. The comments are uninterpreted plain text, so HTML and other
|
||||
annotations such as _this_ will reproduce verbatim and should not be used. One adjustment godoc
|
||||
does do is to display indented text in a fixed-width font, suitable for program snippets.
|
||||
The package comment for the fmt package uses this to good effect.
|
||||
|
||||
|
||||
### Comment First Word as Subject
|
||||
Doc comments work best as complete sentences, which allow a wide variety of automated presentations.
|
||||
The first sentence should be a one-sentence summary that starts with the name being declared.
|
||||
|
||||
|
||||
### Good Package Name
|
||||
It's helpful if everyone using the package can use the same name
|
||||
to refer to its contents, which implies that the package name should
|
||||
be good: short, concise, evocative. By convention, packages are
|
||||
given lower case, single-word names; there should be no need for
|
||||
underscores or mixedCaps. Err on the side of brevity, since everyone
|
||||
using your package will be typing that name. And don't worry about
|
||||
collisions a priori. The package name is only the default name for
|
||||
imports; it need not be unique across all source code, and in the
|
||||
rare case of a collision the importing package can choose a different
|
||||
name to use locally. In any case, confusion is rare because the file
|
||||
name in the import determines just which package is being used.
|
||||
|
||||
|
||||
### Avoid Renaming Imports
|
||||
Avoid renaming imports except to avoid a name collision; good package names
|
||||
should not require renaming. In the event of collision, prefer to rename the
|
||||
most local or project-specific import.
|
||||
|
||||
|
||||
### Context as First Argument
|
||||
Values of the context.Context type carry security credentials, tracing information,
|
||||
deadlines, and cancellation signals across API and process boundaries. Go programs
|
||||
pass Contexts explicitly along the entire function call chain from incoming RPCs
|
||||
and HTTP requests to outgoing requests.
|
||||
|
||||
Most functions that use a Context should accept it as their first parameter.
|
||||
|
||||
|
||||
### Do Not Discard Errors
|
||||
Do not discard errors using _ variables. If a function returns an error,
|
||||
check it to make sure the function succeeded. Handle the error, return it, or,
|
||||
in truly exceptional situations, panic.
|
||||
|
||||
|
||||
### Go Error Format
|
||||
Error strings should not be capitalized (unless beginning with proper nouns
|
||||
or acronyms) or end with punctuation, since they are usually printed following
|
||||
other context. That is, use fmt.Errorf("something bad") not fmt.Errorf("Something bad"),
|
||||
so that log.Printf("Reading %s: %v", filename, err) formats without a spurious
|
||||
capital letter mid-message. This does not apply to logging, which is implicitly
|
||||
line-oriented and not combined inside other messages.
|
||||
|
||||
|
||||
### Use Crypto Rand
|
||||
Do not use package math/rand to generate keys, even
|
||||
throwaway ones. Unseeded, the generator is completely predictable.
|
||||
Seeded with time.Nanoseconds(), there are just a few bits of entropy.
|
||||
Instead, use crypto/rand's Reader, and if you need text, print to
|
||||
hexadecimal or base64
|
||||
|
||||
+384
@@ -0,0 +1,384 @@
|
||||
<!-- use this template to generate the contributor docs with the following command: `$ lingo run docs --template CONTRIBUTING_TEMPLATE.md --output CONTRIBUTING.md` -->
|
||||
# Contributing to excelize
|
||||
|
||||
Want to hack on excelize? Awesome! This page contains information about reporting issues as well as some tips and
|
||||
guidelines useful to experienced open source contributors. Finally, make sure
|
||||
you read our [community guidelines](#community-guidelines) before you
|
||||
start participating.
|
||||
|
||||
## Topics
|
||||
|
||||
* [Reporting Security Issues](#reporting-security-issues)
|
||||
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
|
||||
* [Reporting Issues](#reporting-other-issues)
|
||||
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
|
||||
* [Community Guidelines](#community-guidelines)
|
||||
|
||||
## Reporting security issues
|
||||
|
||||
The excelize maintainers take security seriously. If you discover a security
|
||||
issue, please bring it to their attention right away!
|
||||
|
||||
Please **DO NOT** file a public issue, instead send your report privately to
|
||||
[xuri.me](https://xuri.me).
|
||||
|
||||
Security reports are greatly appreciated and we will publicly thank you for it.
|
||||
We currently do not offer a paid security bounty program, but are not
|
||||
ruling it out in the future.
|
||||
|
||||
## Reporting other issues
|
||||
|
||||
A great way to contribute to the project is to send a detailed report when you
|
||||
encounter an issue. We always appreciate a well-written, thorough bug report,
|
||||
and will thank you for it!
|
||||
|
||||
Check that [our issue database](https://github.com/360EntSecGroup-Skylar/excelize/issues)
|
||||
doesn't already include that problem or suggestion before submitting an issue.
|
||||
If you find a match, you can use the "subscribe" button to get notified on
|
||||
updates. Do *not* leave random "+1" or "I have this too" comments, as they
|
||||
only clutter the discussion, and don't help resolving it. However, if you
|
||||
have ways to reproduce the issue or have additional information that may help
|
||||
resolving the issue, please leave a comment.
|
||||
|
||||
When reporting issues, always include the output of `go env`.
|
||||
|
||||
Also include the steps required to reproduce the problem if possible and
|
||||
applicable. This information will help us review and fix your issue faster.
|
||||
When sending lengthy log-files, consider posting them as a gist [https://gist.github.com](https://gist.github.com).
|
||||
Don't forget to remove sensitive data from your logfiles before posting (you can
|
||||
replace those parts with "REDACTED").
|
||||
|
||||
## Quick contribution tips and guidelines
|
||||
|
||||
This section gives the experienced contributor some tips and guidelines.
|
||||
|
||||
### Pull requests are always welcome
|
||||
|
||||
Not sure if that typo is worth a pull request? Found a bug and know how to fix
|
||||
it? Do it! We will appreciate it. Any significant improvement should be
|
||||
documented as [a GitHub issue](https://github.com/360EntSecGroup-Skylar/excelize/issues) before
|
||||
anybody starts working on it.
|
||||
|
||||
We are always thrilled to receive pull requests. We do our best to process them
|
||||
quickly. If your pull request is not accepted on the first try,
|
||||
don't get discouraged!
|
||||
|
||||
### Design and cleanup proposals
|
||||
|
||||
You can propose new designs for existing excelize features. You can also design
|
||||
entirely new features. We really appreciate contributors who want to refactor or
|
||||
otherwise cleanup our project.
|
||||
|
||||
We try hard to keep excelize lean and focused. Excelize can't do everything for
|
||||
everybody. This means that we might decide against incorporating a new feature.
|
||||
However, there might be a way to implement that feature *on top of* excelize.
|
||||
|
||||
### Conventions
|
||||
|
||||
Fork the repository and make changes on your fork in a feature branch:
|
||||
|
||||
* If it's a bug fix branch, name it XXXX-something where XXXX is the number of
|
||||
the issue.
|
||||
* If it's a feature branch, create an enhancement issue to announce
|
||||
your intentions, and name it XXXX-something where XXXX is the number of the
|
||||
issue.
|
||||
|
||||
Submit unit tests for your changes. Go has a great test framework built in; use
|
||||
it! Take a look at existing tests for inspiration. Run the full test on your branch before
|
||||
submitting a pull request.
|
||||
|
||||
Update the documentation when creating or modifying features. Test your
|
||||
documentation changes for clarity, concision, and correctness, as well as a
|
||||
clean documentation build.
|
||||
|
||||
Write clean code. Universally formatted code promotes ease of writing, reading,
|
||||
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
|
||||
committing your changes. Most editors have plug-ins that do this automatically.
|
||||
|
||||
Pull request descriptions should be as clear as possible and include a reference
|
||||
to all the issues that they address.
|
||||
|
||||
### Successful Changes
|
||||
|
||||
Before contributing large or high impact changes, make the effort to coordinate
|
||||
with the maintainers of the project before submitting a pull request. This
|
||||
prevents you from doing extra work that may or may not be merged.
|
||||
|
||||
Large PRs that are just submitted without any prior communication are unlikely
|
||||
to be successful.
|
||||
|
||||
While pull requests are the methodology for submitting changes to code, changes
|
||||
are much more likely to be accepted if they are accompanied by additional
|
||||
engineering work. While we don't define this explicitly, most of these goals
|
||||
are accomplished through communication of the design goals and subsequent
|
||||
solutions. Often times, it helps to first state the problem before presenting
|
||||
solutions.
|
||||
|
||||
Typically, the best methods of accomplishing this are to submit an issue,
|
||||
stating the problem. This issue can include a problem statement and a
|
||||
checklist with requirements. If solutions are proposed, alternatives should be
|
||||
listed and eliminated. Even if the criteria for elimination of a solution is
|
||||
frivolous, say so.
|
||||
|
||||
Larger changes typically work best with design documents. These are focused on
|
||||
providing context to the design at the time the feature was conceived and can
|
||||
inform future documentation contributions.
|
||||
|
||||
### Commit Messages
|
||||
|
||||
Commit messages must start with a capitalized and short summary
|
||||
written in the imperative, followed by an optional, more detailed explanatory
|
||||
text which is separated from the summary by an empty line.
|
||||
|
||||
Commit messages should follow best practices, including explaining the context
|
||||
of the problem and how it was solved, including in caveats or follow up changes
|
||||
required. They should tell the story of the change and provide readers
|
||||
understanding of what led to it.
|
||||
|
||||
In practice, the best approach to maintaining a nice commit message is to
|
||||
leverage a `git add -p` and `git commit --amend` to formulate a solid
|
||||
changeset. This allows one to piece together a change, as information becomes
|
||||
available.
|
||||
|
||||
If you squash a series of commits, don't just submit that. Re-write the commit
|
||||
message, as if the series of commits was a single stroke of brilliance.
|
||||
|
||||
That said, there is no requirement to have a single commit for a PR, as long as
|
||||
each commit tells the story. For example, if there is a feature that requires a
|
||||
package, it might make sense to have the package in a separate commit then have
|
||||
a subsequent commit that uses it.
|
||||
|
||||
Remember, you're telling part of the story with the commit message. Don't make
|
||||
your chapter weird.
|
||||
|
||||
### Review
|
||||
|
||||
Code review comments may be added to your pull request. Discuss, then make the
|
||||
suggested modifications and push additional commits to your feature branch. Post
|
||||
a comment after pushing. New commits show up in the pull request automatically,
|
||||
but the reviewers are notified only when you comment.
|
||||
|
||||
Pull requests must be cleanly rebased on top of master without multiple branches
|
||||
mixed into the PR.
|
||||
|
||||
**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your
|
||||
feature branch to update your pull request rather than `merge master`.
|
||||
|
||||
Before you make a pull request, squash your commits into logical units of work
|
||||
using `git rebase -i` and `git push -f`. A logical unit of work is a consistent
|
||||
set of patches that should be reviewed together: for example, upgrading the
|
||||
version of a vendored dependency and taking advantage of its now available new
|
||||
feature constitute two separate units of work. Implementing a new function and
|
||||
calling it in another file constitute a single logical unit of work. The very
|
||||
high majority of submissions should have a single commit, so if in doubt: squash
|
||||
down to one.
|
||||
|
||||
After every commit, make sure the test passes. Include documentation
|
||||
changes in the same pull request so that a revert would remove all traces of
|
||||
the feature or fix.
|
||||
|
||||
Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in commits that
|
||||
close an issue. Including references automatically closes the issue on a merge.
|
||||
|
||||
Please see the [Coding Style](#coding-style) for further guidelines.
|
||||
|
||||
### Merge approval
|
||||
|
||||
The excelize maintainers use LGTM (Looks Good To Me) in comments on the code review to
|
||||
indicate acceptance.
|
||||
|
||||
### Sign your work
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the patch. Your
|
||||
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||
it on as an open-source patch. The rules are pretty simple: if you can certify
|
||||
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||
|
||||
```text
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
1 Letterman Drive
|
||||
Suite D4700
|
||||
San Francisco, CA, 94129
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
|
||||
Then you just add a line to every git commit message:
|
||||
|
||||
Signed-off-by: Ri Xu https://xuri.me
|
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
||||
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||
commit automatically with `git commit -s`.
|
||||
|
||||
### How can I become a maintainer
|
||||
|
||||
First, all maintainers have 3 things
|
||||
|
||||
* They share responsibility in the project's success.
|
||||
* They have made a long-term, recurring time investment to improve the project.
|
||||
* They spend that time doing whatever needs to be done, not necessarily what
|
||||
is the most interesting or fun.
|
||||
|
||||
Maintainers are often under-appreciated, because their work is harder to appreciate.
|
||||
It's easy to appreciate a really cool and technically advanced feature. It's harder
|
||||
to appreciate the absence of bugs, the slow but steady improvement in stability,
|
||||
or the reliability of a release process. But those things distinguish a good
|
||||
project from a great one.
|
||||
|
||||
Don't forget: being a maintainer is a time investment. Make sure you
|
||||
will have time to make yourself available. You don't have to be a
|
||||
maintainer to make a difference on the project!
|
||||
|
||||
If you want to become a meintainer, contact [xuri.me](https://xuri.me) and given a introduction of you.
|
||||
|
||||
## Community guidelines
|
||||
|
||||
We want to keep the community awesome, growing and collaborative. We need
|
||||
your help to keep it that way. To help with this we've come up with some general
|
||||
guidelines for the community as a whole:
|
||||
|
||||
* Be nice: Be courteous, respectful and polite to fellow community members:
|
||||
no regional, racial, gender, or other abuse will be tolerated. We like
|
||||
nice people way better than mean ones!
|
||||
|
||||
* Encourage diversity and participation: Make everyone in our community feel
|
||||
welcome, regardless of their background and the extent of their
|
||||
contributions, and do everything possible to encourage participation in
|
||||
our community.
|
||||
|
||||
* Keep it legal: Basically, don't get us in trouble. Share only content that
|
||||
you own, do not share private or sensitive information, and don't break
|
||||
the law.
|
||||
|
||||
* Stay on topic: Make sure that you are posting to the correct channel and
|
||||
avoid off-topic discussions. Remember when you update an issue or respond
|
||||
to an email you are potentially sending to a large number of people. Please
|
||||
consider this before you update. Also remember that nobody likes spam.
|
||||
|
||||
* Don't send email to the maintainers: There's no need to send email to the
|
||||
maintainers to ask them to investigate an issue or to take a look at a
|
||||
pull request. Instead of sending an email, GitHub mentions should be
|
||||
used to ping maintainers to review a pull request, a proposal or an
|
||||
issue.
|
||||
|
||||
### Guideline violations — 3 strikes method
|
||||
|
||||
The point of this section is not to find opportunities to punish people, but we
|
||||
do need a fair way to deal with people who are making our community suck.
|
||||
|
||||
1. First occurrence: We'll give you a friendly, but public reminder that the
|
||||
behavior is inappropriate according to our guidelines.
|
||||
|
||||
2. Second occurrence: We will send you a private message with a warning that
|
||||
any additional violations will result in removal from the community.
|
||||
|
||||
3. Third occurrence: Depending on the violation, we may need to delete or ban
|
||||
your account.
|
||||
|
||||
**Notes:**
|
||||
|
||||
* Obvious spammers are banned on first occurrence. If we don't do this, we'll
|
||||
have spam all over the place.
|
||||
|
||||
* Violations are forgiven after 6 months of good behavior, and we won't hold a
|
||||
grudge.
|
||||
|
||||
* People who commit minor infractions will get some education, rather than
|
||||
hammering them in the 3 strikes process.
|
||||
|
||||
* The rules apply equally to everyone in the community, no matter how much
|
||||
you've contributed.
|
||||
|
||||
* Extreme violations of a threatening, abusive, destructive or illegal nature
|
||||
will be addressed immediately and are not subject to 3 strikes or forgiveness.
|
||||
|
||||
* Contact [xuri.me](https://xuri.me) to report abuse or appeal violations. In the case of
|
||||
appeals, we know that mistakes happen, and we'll work with you to come up with a
|
||||
fair solution if there has been a misunderstanding.
|
||||
|
||||
## Coding Style
|
||||
|
||||
Unless explicitly stated, we follow all coding guidelines from the Go
|
||||
community. While some of these standards may seem arbitrary, they somehow seem
|
||||
to result in a solid, consistent codebase.
|
||||
|
||||
It is possible that the code base does not currently comply with these
|
||||
guidelines. We are not looking for a massive PR that fixes this, since that
|
||||
goes against the spirit of the guidelines. All new contributions should make a
|
||||
best effort to clean up and make the code base better than they left it.
|
||||
Obviously, apply your best judgement. Remember, the goal here is to make the
|
||||
code base easier for humans to navigate and understand. Always keep that in
|
||||
mind when nudging others to comply.
|
||||
|
||||
The rules:
|
||||
|
||||
1. All code should be formatted with `gofmt -s`.
|
||||
2. All code should pass the default levels of
|
||||
[`golint`](https://github.com/golang/lint).
|
||||
3. All code should follow the guidelines covered in [Effective
|
||||
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
|
||||
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||
4. Comment the code. Tell us the why, the history and the context.
|
||||
5. Document _all_ declarations and methods, even private ones. Declare
|
||||
expectations, caveats and anything else that may be important. If a type
|
||||
gets exported, having the comments already there will ensure it's ready.
|
||||
6. Variable name length should be proportional to its context and no longer.
|
||||
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
|
||||
In practice, short methods will have short variable names and globals will
|
||||
have longer names.
|
||||
7. No underscores in package names. If you need a compound name, step back,
|
||||
and re-examine why you need a compound name. If you still think you need a
|
||||
compound name, lose the underscore.
|
||||
8. No utils or helpers packages. If a function is not general enough to
|
||||
warrant its own package, it has not been written generally enough to be a
|
||||
part of a util package. Just leave it unexported and well-documented.
|
||||
9. All tests should run with `go test` and outside tooling should not be
|
||||
required. No, we don't need another unit testing framework. Assertion
|
||||
packages are acceptable if they provide _real_ incremental value.
|
||||
10. Even though we call these "rules" above, they are actually just
|
||||
guidelines. Since you've read all the rules, you now know that.
|
||||
|
||||
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
||||
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
|
||||
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
|
||||
kool-aid is a lot easier than going thirsty.
|
||||
|
||||
## Code Review Comments and Effective Go Guidelines
|
||||
[CodeLingo](https://codelingo.io) automatically checks every pull request against the following guidelines from [Effective Go](https://golang.org/doc/effective_go.html) and [Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||
|
||||
{{range .}}
|
||||
### {{.title}}
|
||||
{{.body}}
|
||||
{{end}}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2016 - 2019 360 Enterprise Security Group, Endpoint Security,
|
||||
inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Excelize nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# PR Details
|
||||
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
## Description
|
||||
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Related Issue
|
||||
|
||||
<!--- This project only accepts pull requests related to open issues -->
|
||||
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
|
||||
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
|
||||
<!--- Please link to the issue here: -->
|
||||
|
||||
## Motivation and Context
|
||||
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
|
||||
## How Has This Been Tested
|
||||
|
||||
<!--- Please describe in detail how you tested your changes. -->
|
||||
<!--- Include details of your testing environment, and the tests you ran to -->
|
||||
<!--- see how your change affects other areas of the code, etc. -->
|
||||
|
||||
## Types of changes
|
||||
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
|
||||
- [ ] Docs change / refactoring / dependency upgrade
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
|
||||
- [ ] My code follows the code style of this project.
|
||||
- [ ] My change requires a change to the documentation.
|
||||
- [ ] I have updated the documentation accordingly.
|
||||
- [ ] I have read the **CONTRIBUTING** document.
|
||||
- [ ] I have added tests to cover my changes.
|
||||
- [ ] All new and existing tests passed.
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
<p align="center"><img width="650" src="./excelize.png" alt="Excelize logo"></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/360EntSecGroup-Skylar/excelize"><img src="https://travis-ci.org/360EntSecGroup-Skylar/excelize.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://codecov.io/gh/360EntSecGroup-Skylar/excelize"><img src="https://codecov.io/gh/360EntSecGroup-Skylar/excelize/branch/master/graph/badge.svg" alt="Code Coverage"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/360EntSecGroup-Skylar/excelize"><img src="https://goreportcard.com/badge/github.com/360EntSecGroup-Skylar/excelize" alt="Go Report Card"></a>
|
||||
<a href="https://godoc.org/github.com/360EntSecGroup-Skylar/excelize"><img src="https://godoc.org/github.com/360EntSecGroup-Skylar/excelize?status.svg" alt="GoDoc"></a>
|
||||
<a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/license-bsd-orange.svg" alt="Licenses"></a>
|
||||
<a href="https://www.paypal.me/xuri"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a>
|
||||
</p>
|
||||
|
||||
# Excelize
|
||||
|
||||
## Introduction
|
||||
|
||||
Excelize is a library written in pure Go and providing a set of functions that allow you to write to and read from XLSX files. Support reads and writes XLSX file generated by Microsoft Excel™ 2007 and later. Support save file without losing original charts of XLSX. This library needs Go version 1.8 or later. The full API docs can be seen using go's built-in documentation tool, or online at [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) and [docs reference](https://xuri.me/excelize/).
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Installation
|
||||
|
||||
```go
|
||||
go get github.com/360EntSecGroup-Skylar/excelize
|
||||
```
|
||||
|
||||
### Create XLSX file
|
||||
|
||||
Here is a minimal example usage that will create XLSX file.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
xlsx := excelize.NewFile()
|
||||
// Create a new sheet.
|
||||
index := xlsx.NewSheet("Sheet2")
|
||||
// Set value of a cell.
|
||||
xlsx.SetCellValue("Sheet2", "A2", "Hello world.")
|
||||
xlsx.SetCellValue("Sheet1", "B2", 100)
|
||||
// Set active sheet of the workbook.
|
||||
xlsx.SetActiveSheet(index)
|
||||
// Save xlsx file by the given path.
|
||||
err := xlsx.SaveAs("./Book1.xlsx")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Reading XLSX file
|
||||
|
||||
The following constitutes the bare to read a XLSX document.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
// Get value from cell by given worksheet name and axis.
|
||||
cell := xlsx.GetCellValue("Sheet1", "B2")
|
||||
fmt.Println(cell)
|
||||
// Get all the rows in the Sheet1.
|
||||
rows := xlsx.GetRows("Sheet1")
|
||||
for _, row := range rows {
|
||||
for _, colCell := range row {
|
||||
fmt.Print(colCell, "\t")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add chart to XLSX file
|
||||
|
||||
With Excelize chart generation and management is as easy as a few lines of code. You can build charts based off data in your worksheet or generate charts without any data in your worksheet at all.
|
||||
|
||||
<p align="center"><img width="650" src="./test/images/chart.png" alt="Excelize"></p>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
|
||||
values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
|
||||
xlsx := excelize.NewFile()
|
||||
for k, v := range categories {
|
||||
xlsx.SetCellValue("Sheet1", k, v)
|
||||
}
|
||||
for k, v := range values {
|
||||
xlsx.SetCellValue("Sheet1", k, v)
|
||||
}
|
||||
xlsx.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`)
|
||||
// Save xlsx file by the given path.
|
||||
err := xlsx.SaveAs("./Book1.xlsx")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Add picture to XLSX file
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
// Insert a picture.
|
||||
err = xlsx.AddPicture("Sheet1", "A2", "./image1.png", "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Insert a picture to worksheet with scaling.
|
||||
err = xlsx.AddPicture("Sheet1", "D2", "./image2.jpg", `{"x_scale": 0.5, "y_scale": 0.5}`)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Insert a picture offset in the cell with printing support.
|
||||
err = xlsx.AddPicture("Sheet1", "H2", "./image3.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Save the xlsx file with the origin path.
|
||||
err = xlsx.Save()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Open a pull request to fix a bug, or open an issue to discuss a new feature or change. XML is compliant with [part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML](http://www.ecma-international.org/publications/standards/Ecma-376.htm).
|
||||
|
||||
## Credits
|
||||
|
||||
Some struct of XML originally by [tealeg/xlsx](https://github.com/tealeg/xlsx).
|
||||
|
||||
## Licenses
|
||||
|
||||
This program is under the terms of the BSD 3-Clause License. See [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause).
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2F360EntSecGroup-Skylar%2Fexcelize?ref=badge_large)
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
<p align="center"><img width="650" src="./excelize.png" alt="Excelize logo"></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/360EntSecGroup-Skylar/excelize"><img src="https://travis-ci.org/360EntSecGroup-Skylar/excelize.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://codecov.io/gh/360EntSecGroup-Skylar/excelize"><img src="https://codecov.io/gh/360EntSecGroup-Skylar/excelize/branch/master/graph/badge.svg" alt="Code Coverage"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/360EntSecGroup-Skylar/excelize"><img src="https://goreportcard.com/badge/github.com/360EntSecGroup-Skylar/excelize" alt="Go Report Card"></a>
|
||||
<a href="https://godoc.org/github.com/360EntSecGroup-Skylar/excelize"><img src="https://godoc.org/github.com/360EntSecGroup-Skylar/excelize?status.svg" alt="GoDoc"></a>
|
||||
<a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/license-bsd-orange.svg" alt="Licenses"></a>
|
||||
<a href="https://www.paypal.me/xuri"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a>
|
||||
</p>
|
||||
|
||||
# Excelize
|
||||
|
||||
## 简介
|
||||
|
||||
Excelize 是 Go 语言编写的用于操作 Office Excel 文档类库,基于 ECMA-376 Office OpenXML 标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的 XLSX 文档。相比较其他的开源类库,Excelize 支持写入原本带有图片(表)、透视表和切片器等复杂样式的文档,还支持向 Excel 文档中插入图片与图表,并且在保存后不会丢失文档原有样式,可以应用于各类报表系统中。使用本类库要求使用的 Go 语言为 1.8 或更高版本,完整的 API 使用文档请访问 [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) 或查看 [参考文档](https://xuri.me/excelize/)。
|
||||
|
||||
## 快速上手
|
||||
|
||||
### 安装
|
||||
|
||||
```go
|
||||
go get github.com/360EntSecGroup-Skylar/excelize
|
||||
```
|
||||
|
||||
### 创建 Excel 文档
|
||||
|
||||
下面是一个创建 Excel 文档的简单例子:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
xlsx := excelize.NewFile()
|
||||
// 创建一个工作表
|
||||
index := xlsx.NewSheet("Sheet2")
|
||||
// 设置单元格的值
|
||||
xlsx.SetCellValue("Sheet2", "A2", "Hello world.")
|
||||
xlsx.SetCellValue("Sheet1", "B2", 100)
|
||||
// 设置工作簿的默认工作表
|
||||
xlsx.SetActiveSheet(index)
|
||||
// 根据指定路径保存文件
|
||||
err := xlsx.SaveAs("./Book1.xlsx")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 读取 Excel 文档
|
||||
|
||||
下面是读取 Excel 文档的例子:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
// 获取工作表中指定单元格的值
|
||||
cell := xlsx.GetCellValue("Sheet1", "B2")
|
||||
fmt.Println(cell)
|
||||
// 获取 Sheet1 上所有单元格
|
||||
rows := xlsx.GetRows("Sheet1")
|
||||
for _, row := range rows {
|
||||
for _, colCell := range row {
|
||||
fmt.Print(colCell, "\t")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 在 Excel 文档中创建图表
|
||||
|
||||
使用 Excelize 生成图表十分简单,仅需几行代码。您可以根据工作表中的已有数据构建图表,或向工作表中添加数据并创建图表。
|
||||
|
||||
<p align="center"><img width="650" src="./test/images/chart.png" alt="Excelize"></p>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
|
||||
values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
|
||||
xlsx := excelize.NewFile()
|
||||
for k, v := range categories {
|
||||
xlsx.SetCellValue("Sheet1", k, v)
|
||||
}
|
||||
for k, v := range values {
|
||||
xlsx.SetCellValue("Sheet1", k, v)
|
||||
}
|
||||
xlsx.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`)
|
||||
// 根据指定路径保存文件
|
||||
err := xlsx.SaveAs("./Book1.xlsx")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 向 Excel 文档中插入图片
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
// 插入图片
|
||||
err = xlsx.AddPicture("Sheet1", "A2", "./image1.png", "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// 在工作表中插入图片,并设置图片的缩放比例
|
||||
err = xlsx.AddPicture("Sheet1", "D2", "./image2.jpg", `{"x_scale": 0.5, "y_scale": 0.5}`)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// 在工作表中插入图片,并设置图片的打印属性
|
||||
err = xlsx.AddPicture("Sheet1", "H2", "./image3.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// 保存文件
|
||||
err = xlsx.Save()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 社区合作
|
||||
|
||||
欢迎您为此项目贡献代码,提出建议或问题、修复 Bug 以及参与讨论对新功能的想法。 XML 符合标准: [part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML](http://www.ecma-international.org/publications/standards/Ecma-376.htm)。
|
||||
|
||||
## 致谢
|
||||
|
||||
本类库中部分 XML 结构体的定义参考了开源项目:[tealeg/xlsx](https://github.com/tealeg/xlsx).
|
||||
|
||||
## 开源许可
|
||||
|
||||
本项目遵循 BSD 3-Clause 开源许可协议,访问 [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 查看许可协议文件。
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2F360EntSecGroup-Skylar%2Fexcelize?ref=badge_large)
|
||||
+599
@@ -0,0 +1,599 @@
|
||||
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// STCellFormulaTypeArray defined the formula is an array formula.
|
||||
STCellFormulaTypeArray = "array"
|
||||
// STCellFormulaTypeDataTable defined the formula is a data table formula.
|
||||
STCellFormulaTypeDataTable = "dataTable"
|
||||
// STCellFormulaTypeNormal defined the formula is a regular cell formula.
|
||||
STCellFormulaTypeNormal = "normal"
|
||||
// STCellFormulaTypeShared defined the formula is part of a shared formula.
|
||||
STCellFormulaTypeShared = "shared"
|
||||
)
|
||||
|
||||
// mergeCellsParser provides a function to check merged cells in worksheet by
|
||||
// given axis.
|
||||
func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) string {
|
||||
axis = strings.ToUpper(axis)
|
||||
if xlsx.MergeCells != nil {
|
||||
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
|
||||
if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
|
||||
axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return axis
|
||||
}
|
||||
|
||||
// SetCellValue provides a function to set value of a cell. The following
|
||||
// shows the supported data types:
|
||||
//
|
||||
// int
|
||||
// int8
|
||||
// int16
|
||||
// int32
|
||||
// int64
|
||||
// uint
|
||||
// uint8
|
||||
// uint16
|
||||
// uint32
|
||||
// uint64
|
||||
// float32
|
||||
// float64
|
||||
// string
|
||||
// []byte
|
||||
// time.Duration
|
||||
// time.Time
|
||||
// bool
|
||||
// nil
|
||||
//
|
||||
// Note that default date format is m/d/yy h:mm of time.Time type value. You can
|
||||
// set numbers format by SetCellStyle() method.
|
||||
func (f *File) SetCellValue(sheet, axis string, value interface{}) {
|
||||
switch t := value.(type) {
|
||||
case float32:
|
||||
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float32)), 'f', -1, 32))
|
||||
case float64:
|
||||
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float64)), 'f', -1, 64))
|
||||
case string:
|
||||
f.SetCellStr(sheet, axis, t)
|
||||
case []byte:
|
||||
f.SetCellStr(sheet, axis, string(t))
|
||||
case time.Duration:
|
||||
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(time.Duration).Seconds()/86400), 'f', -1, 32))
|
||||
f.setDefaultTimeStyle(sheet, axis, 21)
|
||||
case time.Time:
|
||||
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(timeToExcelTime(timeToUTCTime(value.(time.Time)))), 'f', -1, 64))
|
||||
f.setDefaultTimeStyle(sheet, axis, 22)
|
||||
case nil:
|
||||
f.SetCellStr(sheet, axis, "")
|
||||
case bool:
|
||||
f.SetCellBool(sheet, axis, bool(value.(bool)))
|
||||
default:
|
||||
f.setCellIntValue(sheet, axis, value)
|
||||
}
|
||||
}
|
||||
|
||||
// setCellIntValue provides a function to set int value of a cell.
|
||||
func (f *File) setCellIntValue(sheet, axis string, value interface{}) {
|
||||
switch value.(type) {
|
||||
case int:
|
||||
f.SetCellInt(sheet, axis, value.(int))
|
||||
case int8:
|
||||
f.SetCellInt(sheet, axis, int(value.(int8)))
|
||||
case int16:
|
||||
f.SetCellInt(sheet, axis, int(value.(int16)))
|
||||
case int32:
|
||||
f.SetCellInt(sheet, axis, int(value.(int32)))
|
||||
case int64:
|
||||
f.SetCellInt(sheet, axis, int(value.(int64)))
|
||||
case uint:
|
||||
f.SetCellInt(sheet, axis, int(value.(uint)))
|
||||
case uint8:
|
||||
f.SetCellInt(sheet, axis, int(value.(uint8)))
|
||||
case uint16:
|
||||
f.SetCellInt(sheet, axis, int(value.(uint16)))
|
||||
case uint32:
|
||||
f.SetCellInt(sheet, axis, int(value.(uint32)))
|
||||
case uint64:
|
||||
f.SetCellInt(sheet, axis, int(value.(uint64)))
|
||||
default:
|
||||
f.SetCellStr(sheet, axis, fmt.Sprintf("%v", value))
|
||||
}
|
||||
}
|
||||
|
||||
// SetCellBool provides a function to set bool type value of a cell by given
|
||||
// worksheet name, cell coordinates and cell value.
|
||||
func (f *File) SetCellBool(sheet, axis string, value bool) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
xAxis := row - 1
|
||||
yAxis := TitleToNumber(col)
|
||||
|
||||
rows := xAxis + 1
|
||||
cell := yAxis + 1
|
||||
|
||||
completeRow(xlsx, rows, cell)
|
||||
completeCol(xlsx, rows, cell)
|
||||
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].T = "b"
|
||||
if value {
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].V = "1"
|
||||
} else {
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].V = "0"
|
||||
}
|
||||
}
|
||||
|
||||
// GetCellValue provides a function to get formatted value from cell by given
|
||||
// worksheet name and axis in XLSX file. If it is possible to apply a format
|
||||
// to the cell value, it will do so, if not then an error will be returned,
|
||||
// along with the raw value of the cell.
|
||||
func (f *File) GetCellValue(sheet, axis string) string {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
xAxis := row - 1
|
||||
rows := len(xlsx.SheetData.Row)
|
||||
if rows > 1 {
|
||||
lastRow := xlsx.SheetData.Row[rows-1].R
|
||||
if lastRow >= rows {
|
||||
rows = lastRow
|
||||
}
|
||||
}
|
||||
if rows < xAxis {
|
||||
return ""
|
||||
}
|
||||
for k := range xlsx.SheetData.Row {
|
||||
if xlsx.SheetData.Row[k].R == row {
|
||||
for i := range xlsx.SheetData.Row[k].C {
|
||||
if axis == xlsx.SheetData.Row[k].C[i].R {
|
||||
val, _ := xlsx.SheetData.Row[k].C[i].getValueFrom(f, f.sharedStringsReader())
|
||||
return val
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// formattedValue provides a function to returns a value after formatted. If
|
||||
// it is possible to apply a format to the cell value, it will do so, if not
|
||||
// then an error will be returned, along with the raw value of the cell.
|
||||
func (f *File) formattedValue(s int, v string) string {
|
||||
if s == 0 {
|
||||
return v
|
||||
}
|
||||
styleSheet := f.stylesReader()
|
||||
ok := builtInNumFmtFunc[styleSheet.CellXfs.Xf[s].NumFmtID]
|
||||
if ok != nil {
|
||||
return ok(styleSheet.CellXfs.Xf[s].NumFmtID, v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetCellStyle provides a function to get cell style index by given worksheet
|
||||
// name and cell coordinates.
|
||||
func (f *File) GetCellStyle(sheet, axis string) int {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
xAxis := row - 1
|
||||
yAxis := TitleToNumber(col)
|
||||
|
||||
rows := xAxis + 1
|
||||
cell := yAxis + 1
|
||||
|
||||
completeRow(xlsx, rows, cell)
|
||||
completeCol(xlsx, rows, cell)
|
||||
|
||||
return f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||
}
|
||||
|
||||
// GetCellFormula provides a function to get formula from cell by given
|
||||
// worksheet name and axis in XLSX file.
|
||||
func (f *File) GetCellFormula(sheet, axis string) string {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
xAxis := row - 1
|
||||
rows := len(xlsx.SheetData.Row)
|
||||
if rows > 1 {
|
||||
lastRow := xlsx.SheetData.Row[rows-1].R
|
||||
if lastRow >= rows {
|
||||
rows = lastRow
|
||||
}
|
||||
}
|
||||
if rows < xAxis {
|
||||
return ""
|
||||
}
|
||||
for k := range xlsx.SheetData.Row {
|
||||
if xlsx.SheetData.Row[k].R == row {
|
||||
for i := range xlsx.SheetData.Row[k].C {
|
||||
if axis == xlsx.SheetData.Row[k].C[i].R {
|
||||
if xlsx.SheetData.Row[k].C[i].F == nil {
|
||||
continue
|
||||
}
|
||||
if xlsx.SheetData.Row[k].C[i].F.T == STCellFormulaTypeShared {
|
||||
return getSharedForumula(xlsx, xlsx.SheetData.Row[k].C[i].F.Si)
|
||||
}
|
||||
return xlsx.SheetData.Row[k].C[i].F.Content
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getSharedForumula find a cell contains the same formula as another cell,
|
||||
// the "shared" value can be used for the t attribute and the si attribute can
|
||||
// be used to refer to the cell containing the formula. Two formulas are
|
||||
// considered to be the same when their respective representations in
|
||||
// R1C1-reference notation, are the same.
|
||||
//
|
||||
// Note that this function not validate ref tag to check the cell if or not in
|
||||
// allow area, and always return origin shared formula.
|
||||
func getSharedForumula(xlsx *xlsxWorksheet, si string) string {
|
||||
for k := range xlsx.SheetData.Row {
|
||||
for i := range xlsx.SheetData.Row[k].C {
|
||||
if xlsx.SheetData.Row[k].C[i].F == nil {
|
||||
continue
|
||||
}
|
||||
if xlsx.SheetData.Row[k].C[i].F.T != STCellFormulaTypeShared {
|
||||
continue
|
||||
}
|
||||
if xlsx.SheetData.Row[k].C[i].F.Si != si {
|
||||
continue
|
||||
}
|
||||
if xlsx.SheetData.Row[k].C[i].F.Ref != "" {
|
||||
return xlsx.SheetData.Row[k].C[i].F.Content
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// SetCellFormula provides a function to set cell formula by given string and
|
||||
// worksheet name.
|
||||
func (f *File) SetCellFormula(sheet, axis, formula string) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
xAxis := row - 1
|
||||
yAxis := TitleToNumber(col)
|
||||
|
||||
rows := xAxis + 1
|
||||
cell := yAxis + 1
|
||||
|
||||
completeRow(xlsx, rows, cell)
|
||||
completeCol(xlsx, rows, cell)
|
||||
|
||||
if xlsx.SheetData.Row[xAxis].C[yAxis].F != nil {
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].F.Content = formula
|
||||
} else {
|
||||
f := xlsxF{
|
||||
Content: formula,
|
||||
}
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].F = &f
|
||||
}
|
||||
}
|
||||
|
||||
// SetCellHyperLink provides a function to set cell hyperlink by given
|
||||
// worksheet name and link URL address. LinkType defines two types of
|
||||
// hyperlink "External" for web site or "Location" for moving to one of cell
|
||||
// in this workbook. The below is example for external link.
|
||||
//
|
||||
// xlsx.SetCellHyperLink("Sheet1", "A3", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
|
||||
// // Set underline and font color style for the cell.
|
||||
// style, _ := xlsx.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
|
||||
// xlsx.SetCellStyle("Sheet1", "A3", "A3", style)
|
||||
//
|
||||
// A this is another example for "Location":
|
||||
//
|
||||
// xlsx.SetCellHyperLink("Sheet1", "A3", "Sheet1!A40", "Location")
|
||||
//
|
||||
func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
linkTypes := map[string]xlsxHyperlink{
|
||||
"External": {},
|
||||
"Location": {Location: link},
|
||||
}
|
||||
hyperlink, ok := linkTypes[linkType]
|
||||
if !ok || axis == "" {
|
||||
return
|
||||
}
|
||||
hyperlink.Ref = axis
|
||||
if linkType == "External" {
|
||||
rID := f.addSheetRelationships(sheet, SourceRelationshipHyperLink, link, linkType)
|
||||
hyperlink.RID = "rId" + strconv.Itoa(rID)
|
||||
}
|
||||
if xlsx.Hyperlinks == nil {
|
||||
xlsx.Hyperlinks = &xlsxHyperlinks{}
|
||||
}
|
||||
xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, hyperlink)
|
||||
}
|
||||
|
||||
// GetCellHyperLink provides a function to get cell hyperlink by given
|
||||
// worksheet name and axis. Boolean type value link will be ture if the cell
|
||||
// has a hyperlink and the target is the address of the hyperlink. Otherwise,
|
||||
// the value of link will be false and the value of the target will be a blank
|
||||
// string. For example get hyperlink of Sheet1!H6:
|
||||
//
|
||||
// link, target := xlsx.GetCellHyperLink("Sheet1", "H6")
|
||||
//
|
||||
func (f *File) GetCellHyperLink(sheet, axis string) (bool, string) {
|
||||
var link bool
|
||||
var target string
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
if xlsx.Hyperlinks == nil || axis == "" {
|
||||
return link, target
|
||||
}
|
||||
for h := range xlsx.Hyperlinks.Hyperlink {
|
||||
if xlsx.Hyperlinks.Hyperlink[h].Ref == axis {
|
||||
link = true
|
||||
target = xlsx.Hyperlinks.Hyperlink[h].Location
|
||||
if xlsx.Hyperlinks.Hyperlink[h].RID != "" {
|
||||
target = f.getSheetRelationshipsTargetByID(sheet, xlsx.Hyperlinks.Hyperlink[h].RID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return link, target
|
||||
}
|
||||
|
||||
// MergeCell provides a function to merge cells by given coordinate area and
|
||||
// sheet name. For example create a merged cell of D3:E9 on Sheet1:
|
||||
//
|
||||
// xlsx.MergeCell("Sheet1", "D3", "E9")
|
||||
//
|
||||
// If you create a merged cell that overlaps with another existing merged cell,
|
||||
// those merged cells that already exist will be removed.
|
||||
func (f *File) MergeCell(sheet, hcell, vcell string) {
|
||||
if hcell == vcell {
|
||||
return
|
||||
}
|
||||
|
||||
hcell = strings.ToUpper(hcell)
|
||||
vcell = strings.ToUpper(vcell)
|
||||
|
||||
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
|
||||
hcol := string(strings.Map(letterOnlyMapF, hcell))
|
||||
hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
|
||||
hyAxis := hrow - 1
|
||||
hxAxis := TitleToNumber(hcol)
|
||||
|
||||
vcol := string(strings.Map(letterOnlyMapF, vcell))
|
||||
vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
|
||||
vyAxis := vrow - 1
|
||||
vxAxis := TitleToNumber(vcol)
|
||||
|
||||
if vxAxis < hxAxis {
|
||||
hcell, vcell = vcell, hcell
|
||||
vxAxis, hxAxis = hxAxis, vxAxis
|
||||
}
|
||||
|
||||
if vyAxis < hyAxis {
|
||||
hcell, vcell = vcell, hcell
|
||||
vyAxis, hyAxis = hyAxis, vyAxis
|
||||
}
|
||||
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
if xlsx.MergeCells != nil {
|
||||
mergeCell := xlsxMergeCell{}
|
||||
// Correct the coordinate area, such correct C1:B3 to B1:C3.
|
||||
mergeCell.Ref = ToAlphaString(hxAxis) + strconv.Itoa(hyAxis+1) + ":" + ToAlphaString(vxAxis) + strconv.Itoa(vyAxis+1)
|
||||
// Delete the merged cells of the overlapping area.
|
||||
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
|
||||
if checkCellInArea(hcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0], mergeCell.Ref) {
|
||||
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
|
||||
} else if checkCellInArea(vcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[1], mergeCell.Ref) {
|
||||
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
|
||||
}
|
||||
}
|
||||
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells, &mergeCell)
|
||||
} else {
|
||||
mergeCell := xlsxMergeCell{}
|
||||
// Correct the coordinate area, such correct C1:B3 to B1:C3.
|
||||
mergeCell.Ref = ToAlphaString(hxAxis) + strconv.Itoa(hyAxis+1) + ":" + ToAlphaString(vxAxis) + strconv.Itoa(vyAxis+1)
|
||||
mergeCells := xlsxMergeCells{}
|
||||
mergeCells.Cells = append(mergeCells.Cells, &mergeCell)
|
||||
xlsx.MergeCells = &mergeCells
|
||||
}
|
||||
}
|
||||
|
||||
// SetCellInt provides a function to set int type value of a cell by given
|
||||
// worksheet name, cell coordinates and cell value.
|
||||
func (f *File) SetCellInt(sheet, axis string, value int) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
xAxis := row - 1
|
||||
yAxis := TitleToNumber(col)
|
||||
|
||||
rows := xAxis + 1
|
||||
cell := yAxis + 1
|
||||
|
||||
completeRow(xlsx, rows, cell)
|
||||
completeCol(xlsx, rows, cell)
|
||||
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)
|
||||
}
|
||||
|
||||
// prepareCellStyle provides a function to prepare style index of cell in
|
||||
// worksheet by given column index and style index.
|
||||
func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
|
||||
if xlsx.Cols != nil && style == 0 {
|
||||
for _, v := range xlsx.Cols.Col {
|
||||
if v.Min <= col && col <= v.Max {
|
||||
style = v.Style
|
||||
}
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
|
||||
// SetCellStr provides a function to set string type value of a cell. Total
|
||||
// number of characters that a cell can contain 32767 characters.
|
||||
func (f *File) SetCellStr(sheet, axis, value string) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
if len(value) > 32767 {
|
||||
value = value[0:32767]
|
||||
}
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
xAxis := row - 1
|
||||
yAxis := TitleToNumber(col)
|
||||
|
||||
rows := xAxis + 1
|
||||
cell := yAxis + 1
|
||||
|
||||
completeRow(xlsx, rows, cell)
|
||||
completeCol(xlsx, rows, cell)
|
||||
|
||||
// Leading space(s) character detection.
|
||||
if len(value) > 0 {
|
||||
if value[0] == 32 {
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].XMLSpace = xml.Attr{
|
||||
Name: xml.Name{Space: NameSpaceXML, Local: "space"},
|
||||
Value: "preserve",
|
||||
}
|
||||
}
|
||||
}
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].T = "str"
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].V = value
|
||||
}
|
||||
|
||||
// SetCellDefault provides a function to set string type value of a cell as
|
||||
// default format without escaping the cell.
|
||||
func (f *File) SetCellDefault(sheet, axis, value string) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
xAxis := row - 1
|
||||
yAxis := TitleToNumber(col)
|
||||
|
||||
rows := xAxis + 1
|
||||
cell := yAxis + 1
|
||||
|
||||
completeRow(xlsx, rows, cell)
|
||||
completeCol(xlsx, rows, cell)
|
||||
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
|
||||
xlsx.SheetData.Row[xAxis].C[yAxis].V = value
|
||||
}
|
||||
|
||||
// SetSheetRow writes an array to row by given worksheet name, starting
|
||||
// coordinate and a pointer to array type 'slice'. For example, writes an
|
||||
// array to row 6 start with the cell B6 on Sheet1:
|
||||
//
|
||||
// xlsx.SetSheetRow("Sheet1", "B6", &[]interface{}{"1", nil, 2})
|
||||
//
|
||||
func (f *File) SetSheetRow(sheet, axis string, slice interface{}) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
axis = f.mergeCellsParser(xlsx, axis)
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Make sure 'slice' is a Ptr to Slice
|
||||
v := reflect.ValueOf(slice)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
return
|
||||
}
|
||||
v = v.Elem()
|
||||
if v.Kind() != reflect.Slice {
|
||||
return
|
||||
}
|
||||
|
||||
xAxis := row - 1
|
||||
yAxis := TitleToNumber(col)
|
||||
|
||||
rows := xAxis + 1
|
||||
cell := yAxis + 1
|
||||
|
||||
completeRow(xlsx, rows, cell)
|
||||
completeCol(xlsx, rows, cell)
|
||||
|
||||
idx := 0
|
||||
for i := cell - 1; i < v.Len()+cell-1; i++ {
|
||||
c := ToAlphaString(i) + strconv.Itoa(row)
|
||||
f.SetCellValue(sheet, c, v.Index(idx).Interface())
|
||||
idx++
|
||||
}
|
||||
}
|
||||
|
||||
// checkCellInArea provides a function to determine if a given coordinate is
|
||||
// within an area.
|
||||
func checkCellInArea(cell, area string) bool {
|
||||
cell = strings.ToUpper(cell)
|
||||
area = strings.ToUpper(area)
|
||||
|
||||
ref := strings.Split(area, ":")
|
||||
if len(ref) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
from := ref[0]
|
||||
to := ref[1]
|
||||
|
||||
col, row := getCellColRow(cell)
|
||||
fromCol, fromRow := getCellColRow(from)
|
||||
toCol, toRow := getCellColRow(to)
|
||||
|
||||
return axisLowerOrEqualThan(fromCol, col) && axisLowerOrEqualThan(col, toCol) && axisLowerOrEqualThan(fromRow, row) && axisLowerOrEqualThan(row, toRow)
|
||||
}
|
||||
+1291
File diff suppressed because it is too large
Load Diff
+3
@@ -0,0 +1,3 @@
|
||||
tenets:
|
||||
- import: codelingo/effective-go
|
||||
- import: codelingo/code-review-comments
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Define the default cell size and EMU unit of measurement.
|
||||
const (
|
||||
defaultColWidthPixels float64 = 64
|
||||
defaultRowHeightPixels float64 = 20
|
||||
EMU int = 9525
|
||||
)
|
||||
|
||||
// GetColVisible provides a function to get visible of a single column by given
|
||||
// worksheet name and column name. For example, get visible state of column D
|
||||
// in Sheet1:
|
||||
//
|
||||
// xlsx.GetColVisible("Sheet1", "D")
|
||||
//
|
||||
func (f *File) GetColVisible(sheet, column string) bool {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
col := TitleToNumber(strings.ToUpper(column)) + 1
|
||||
visible := true
|
||||
if xlsx.Cols == nil {
|
||||
return visible
|
||||
}
|
||||
for c := range xlsx.Cols.Col {
|
||||
if xlsx.Cols.Col[c].Min <= col && col <= xlsx.Cols.Col[c].Max {
|
||||
visible = !xlsx.Cols.Col[c].Hidden
|
||||
}
|
||||
}
|
||||
return visible
|
||||
}
|
||||
|
||||
// SetColVisible provides a function to set visible of a single column by given
|
||||
// worksheet name and column name. For example, hide column D in Sheet1:
|
||||
//
|
||||
// xlsx.SetColVisible("Sheet1", "D", false)
|
||||
//
|
||||
func (f *File) SetColVisible(sheet, column string, visible bool) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
c := TitleToNumber(strings.ToUpper(column)) + 1
|
||||
col := xlsxCol{
|
||||
Min: c,
|
||||
Max: c,
|
||||
Hidden: !visible,
|
||||
CustomWidth: true,
|
||||
}
|
||||
if xlsx.Cols == nil {
|
||||
cols := xlsxCols{}
|
||||
cols.Col = append(cols.Col, col)
|
||||
xlsx.Cols = &cols
|
||||
return
|
||||
}
|
||||
for v := range xlsx.Cols.Col {
|
||||
if xlsx.Cols.Col[v].Min <= c && c <= xlsx.Cols.Col[v].Max {
|
||||
col = xlsx.Cols.Col[v]
|
||||
}
|
||||
}
|
||||
col.Min = c
|
||||
col.Max = c
|
||||
col.Hidden = !visible
|
||||
col.CustomWidth = true
|
||||
xlsx.Cols.Col = append(xlsx.Cols.Col, col)
|
||||
}
|
||||
|
||||
// GetColOutlineLevel provides a function to get outline level of a single
|
||||
// column by given worksheet name and column name. For example, get outline
|
||||
// level of column D in Sheet1:
|
||||
//
|
||||
// xlsx.GetColOutlineLevel("Sheet1", "D")
|
||||
//
|
||||
func (f *File) GetColOutlineLevel(sheet, column string) uint8 {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
col := TitleToNumber(strings.ToUpper(column)) + 1
|
||||
level := uint8(0)
|
||||
if xlsx.Cols == nil {
|
||||
return level
|
||||
}
|
||||
for c := range xlsx.Cols.Col {
|
||||
if xlsx.Cols.Col[c].Min <= col && col <= xlsx.Cols.Col[c].Max {
|
||||
level = xlsx.Cols.Col[c].OutlineLevel
|
||||
}
|
||||
}
|
||||
return level
|
||||
}
|
||||
|
||||
// SetColOutlineLevel provides a function to set outline level of a single
|
||||
// column by given worksheet name and column name. For example, set outline
|
||||
// level of column D in Sheet1 to 2:
|
||||
//
|
||||
// xlsx.SetColOutlineLevel("Sheet1", "D", 2)
|
||||
//
|
||||
func (f *File) SetColOutlineLevel(sheet, column string, level uint8) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
c := TitleToNumber(strings.ToUpper(column)) + 1
|
||||
col := xlsxCol{
|
||||
Min: c,
|
||||
Max: c,
|
||||
OutlineLevel: level,
|
||||
CustomWidth: true,
|
||||
}
|
||||
if xlsx.Cols == nil {
|
||||
cols := xlsxCols{}
|
||||
cols.Col = append(cols.Col, col)
|
||||
xlsx.Cols = &cols
|
||||
return
|
||||
}
|
||||
for v := range xlsx.Cols.Col {
|
||||
if xlsx.Cols.Col[v].Min <= c && c <= xlsx.Cols.Col[v].Max {
|
||||
col = xlsx.Cols.Col[v]
|
||||
}
|
||||
}
|
||||
col.Min = c
|
||||
col.Max = c
|
||||
col.OutlineLevel = level
|
||||
col.CustomWidth = true
|
||||
xlsx.Cols.Col = append(xlsx.Cols.Col, col)
|
||||
}
|
||||
|
||||
// SetColWidth provides a function to set the width of a single column or
|
||||
// multiple columns. For example:
|
||||
//
|
||||
// xlsx := excelize.NewFile()
|
||||
// xlsx.SetColWidth("Sheet1", "A", "H", 20)
|
||||
// err := xlsx.Save()
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//
|
||||
func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) {
|
||||
min := TitleToNumber(strings.ToUpper(startcol)) + 1
|
||||
max := TitleToNumber(strings.ToUpper(endcol)) + 1
|
||||
if min > max {
|
||||
min, max = max, min
|
||||
}
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
col := xlsxCol{
|
||||
Min: min,
|
||||
Max: max,
|
||||
Width: width,
|
||||
CustomWidth: true,
|
||||
}
|
||||
if xlsx.Cols != nil {
|
||||
xlsx.Cols.Col = append(xlsx.Cols.Col, col)
|
||||
} else {
|
||||
cols := xlsxCols{}
|
||||
cols.Col = append(cols.Col, col)
|
||||
xlsx.Cols = &cols
|
||||
}
|
||||
}
|
||||
|
||||
// positionObjectPixels calculate the vertices that define the position of a
|
||||
// graphical object within the worksheet in pixels.
|
||||
//
|
||||
// +------------+------------+
|
||||
// | A | B |
|
||||
// +-----+------------+------------+
|
||||
// | |(x1,y1) | |
|
||||
// | 1 |(A1)._______|______ |
|
||||
// | | | | |
|
||||
// | | | | |
|
||||
// +-----+----| OBJECT |-----+
|
||||
// | | | | |
|
||||
// | 2 | |______________. |
|
||||
// | | | (B2)|
|
||||
// | | | (x2,y2)|
|
||||
// +-----+------------+------------+
|
||||
//
|
||||
// Example of an object that covers some of the area from cell A1 to B2.
|
||||
//
|
||||
// Based on the width and height of the object we need to calculate 8 vars:
|
||||
//
|
||||
// colStart, rowStart, colEnd, rowEnd, x1, y1, x2, y2.
|
||||
//
|
||||
// We also calculate the absolute x and y position of the top left vertex of
|
||||
// the object. This is required for images.
|
||||
//
|
||||
// The width and height of the cells that the object occupies can be
|
||||
// variable and have to be taken into account.
|
||||
//
|
||||
// The values of col_start and row_start are passed in from the calling
|
||||
// function. The values of col_end and row_end are calculated by
|
||||
// subtracting the width and height of the object from the width and
|
||||
// height of the underlying cells.
|
||||
//
|
||||
// colStart # Col containing upper left corner of object.
|
||||
// x1 # Distance to left side of object.
|
||||
//
|
||||
// rowStart # Row containing top left corner of object.
|
||||
// y1 # Distance to top of object.
|
||||
//
|
||||
// colEnd # Col containing lower right corner of object.
|
||||
// x2 # Distance to right side of object.
|
||||
//
|
||||
// rowEnd # Row containing bottom right corner of object.
|
||||
// y2 # Distance to bottom of object.
|
||||
//
|
||||
// width # Width of object frame.
|
||||
// height # Height of object frame.
|
||||
//
|
||||
// xAbs # Absolute distance to left side of object.
|
||||
// yAbs # Absolute distance to top side of object.
|
||||
//
|
||||
func (f *File) positionObjectPixels(sheet string, colStart, rowStart, x1, y1, width, height int) (int, int, int, int, int, int, int, int) {
|
||||
xAbs := 0
|
||||
yAbs := 0
|
||||
|
||||
// Calculate the absolute x offset of the top-left vertex.
|
||||
for colID := 1; colID <= colStart; colID++ {
|
||||
xAbs += f.getColWidth(sheet, colID)
|
||||
}
|
||||
xAbs += x1
|
||||
|
||||
// Calculate the absolute y offset of the top-left vertex.
|
||||
// Store the column change to allow optimisations.
|
||||
for rowID := 1; rowID <= rowStart; rowID++ {
|
||||
yAbs += f.getRowHeight(sheet, rowID)
|
||||
}
|
||||
yAbs += y1
|
||||
|
||||
// Adjust start column for offsets that are greater than the col width.
|
||||
for x1 >= f.getColWidth(sheet, colStart) {
|
||||
x1 -= f.getColWidth(sheet, colStart)
|
||||
colStart++
|
||||
}
|
||||
|
||||
// Adjust start row for offsets that are greater than the row height.
|
||||
for y1 >= f.getRowHeight(sheet, rowStart) {
|
||||
y1 -= f.getRowHeight(sheet, rowStart)
|
||||
rowStart++
|
||||
}
|
||||
|
||||
// Initialise end cell to the same as the start cell.
|
||||
colEnd := colStart
|
||||
rowEnd := rowStart
|
||||
|
||||
width += x1
|
||||
height += y1
|
||||
|
||||
// Subtract the underlying cell widths to find end cell of the object.
|
||||
for width >= f.getColWidth(sheet, colEnd) {
|
||||
colEnd++
|
||||
width -= f.getColWidth(sheet, colEnd)
|
||||
}
|
||||
|
||||
// Subtract the underlying cell heights to find end cell of the object.
|
||||
for height >= f.getRowHeight(sheet, rowEnd) {
|
||||
rowEnd++
|
||||
height -= f.getRowHeight(sheet, rowEnd)
|
||||
}
|
||||
|
||||
// The end vertices are whatever is left from the width and height.
|
||||
x2 := width
|
||||
y2 := height
|
||||
return colStart, rowStart, xAbs, yAbs, colEnd, rowEnd, x2, y2
|
||||
}
|
||||
|
||||
// getColWidth provides a function to get column width in pixels by given
|
||||
// sheet name and column index.
|
||||
func (f *File) getColWidth(sheet string, col int) int {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
if xlsx.Cols != nil {
|
||||
var width float64
|
||||
for _, v := range xlsx.Cols.Col {
|
||||
if v.Min <= col && col <= v.Max {
|
||||
width = v.Width
|
||||
}
|
||||
}
|
||||
if width != 0 {
|
||||
return int(convertColWidthToPixels(width))
|
||||
}
|
||||
}
|
||||
// Optimisation for when the column widths haven't changed.
|
||||
return int(defaultColWidthPixels)
|
||||
}
|
||||
|
||||
// GetColWidth provides a function to get column width by given worksheet name
|
||||
// and column index.
|
||||
func (f *File) GetColWidth(sheet, column string) float64 {
|
||||
col := TitleToNumber(strings.ToUpper(column)) + 1
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
if xlsx.Cols != nil {
|
||||
var width float64
|
||||
for _, v := range xlsx.Cols.Col {
|
||||
if v.Min <= col && col <= v.Max {
|
||||
width = v.Width
|
||||
}
|
||||
}
|
||||
if width != 0 {
|
||||
return width
|
||||
}
|
||||
}
|
||||
// Optimisation for when the column widths haven't changed.
|
||||
return defaultColWidthPixels
|
||||
}
|
||||
|
||||
// InsertCol provides a function to insert a new column before given column
|
||||
// index. For example, create a new column before column C in Sheet1:
|
||||
//
|
||||
// xlsx.InsertCol("Sheet1", "C")
|
||||
//
|
||||
func (f *File) InsertCol(sheet, column string) {
|
||||
col := TitleToNumber(strings.ToUpper(column))
|
||||
f.adjustHelper(sheet, col, -1, 1)
|
||||
}
|
||||
|
||||
// RemoveCol provides a function to remove single column by given worksheet
|
||||
// name and column index. For example, remove column C in Sheet1:
|
||||
//
|
||||
// xlsx.RemoveCol("Sheet1", "C")
|
||||
//
|
||||
func (f *File) RemoveCol(sheet, column string) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
for r := range xlsx.SheetData.Row {
|
||||
for k, v := range xlsx.SheetData.Row[r].C {
|
||||
axis := v.R
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
if col == column {
|
||||
xlsx.SheetData.Row[r].C = append(xlsx.SheetData.Row[r].C[:k], xlsx.SheetData.Row[r].C[k+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
col := TitleToNumber(strings.ToUpper(column))
|
||||
f.adjustHelper(sheet, col, -1, -1)
|
||||
}
|
||||
|
||||
// completeCol provieds function to completion column element tags of XML in a
|
||||
// sheet.
|
||||
func completeCol(xlsx *xlsxWorksheet, row, cell int) {
|
||||
buffer := bytes.Buffer{}
|
||||
for r := range xlsx.SheetData.Row {
|
||||
if len(xlsx.SheetData.Row[r].C) < cell {
|
||||
start := len(xlsx.SheetData.Row[r].C)
|
||||
for iii := start; iii < cell; iii++ {
|
||||
buffer.WriteString(ToAlphaString(iii))
|
||||
buffer.WriteString(strconv.Itoa(r + 1))
|
||||
xlsx.SheetData.Row[r].C = append(xlsx.SheetData.Row[r].C, xlsxC{
|
||||
R: buffer.String(),
|
||||
})
|
||||
buffer.Reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convertColWidthToPixels provieds function to convert the width of a cell
|
||||
// from user's units to pixels. Excel rounds the column width to the nearest
|
||||
// pixel. If the width hasn't been set by the user we use the default value.
|
||||
// If the column is hidden it has a value of zero.
|
||||
func convertColWidthToPixels(width float64) float64 {
|
||||
var padding float64 = 5
|
||||
var pixels float64
|
||||
var maxDigitWidth float64 = 7
|
||||
if width == 0 {
|
||||
return pixels
|
||||
}
|
||||
if width < 1 {
|
||||
pixels = (width * 12) + 0.5
|
||||
return math.Ceil(pixels)
|
||||
}
|
||||
pixels = (width*maxDigitWidth + 0.5) + padding
|
||||
return math.Ceil(pixels)
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseFormatCommentsSet provides a function to parse the format settings of
|
||||
// the comment with default value.
|
||||
func parseFormatCommentsSet(formatSet string) (*formatComment, error) {
|
||||
format := formatComment{
|
||||
Author: "Author:",
|
||||
Text: " ",
|
||||
}
|
||||
err := json.Unmarshal([]byte(formatSet), &format)
|
||||
return &format, err
|
||||
}
|
||||
|
||||
// GetComments retrieves all comments and returns a map of worksheet name to
|
||||
// the worksheet comments.
|
||||
func (f *File) GetComments() (comments map[string][]Comment) {
|
||||
comments = map[string][]Comment{}
|
||||
for n := range f.sheetMap {
|
||||
commentID := f.GetSheetIndex(n)
|
||||
commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
|
||||
c, ok := f.XLSX[commentsXML]
|
||||
if ok {
|
||||
d := xlsxComments{}
|
||||
xml.Unmarshal([]byte(c), &d)
|
||||
sheetComments := []Comment{}
|
||||
for _, comment := range d.CommentList.Comment {
|
||||
sheetComment := Comment{}
|
||||
if comment.AuthorID < len(d.Authors) {
|
||||
sheetComment.Author = d.Authors[comment.AuthorID].Author
|
||||
}
|
||||
sheetComment.Ref = comment.Ref
|
||||
sheetComment.AuthorID = comment.AuthorID
|
||||
for _, text := range comment.Text.R {
|
||||
sheetComment.Text += text.T
|
||||
}
|
||||
sheetComments = append(sheetComments, sheetComment)
|
||||
}
|
||||
comments[n] = sheetComments
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AddComment provides the method to add comment in a sheet by given worksheet
|
||||
// index, cell and format set (such as author and text). Note that the max
|
||||
// author length is 255 and the max text length is 32512. For example, add a
|
||||
// comment in Sheet1!$A$30:
|
||||
//
|
||||
// xlsx.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`)
|
||||
//
|
||||
func (f *File) AddComment(sheet, cell, format string) error {
|
||||
formatSet, err := parseFormatCommentsSet(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Read sheet data.
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
commentID := f.countComments() + 1
|
||||
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(commentID) + ".vml"
|
||||
sheetRelationshipsComments := "../comments" + strconv.Itoa(commentID) + ".xml"
|
||||
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(commentID) + ".vml"
|
||||
if xlsx.LegacyDrawing != nil {
|
||||
// The worksheet already has a comments relationships, use the relationships drawing ../drawings/vmlDrawing%d.vml.
|
||||
sheetRelationshipsDrawingVML = f.getSheetRelationshipsTargetByID(sheet, xlsx.LegacyDrawing.RID)
|
||||
commentID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingVML, "../drawings/vmlDrawing"), ".vml"))
|
||||
drawingVML = strings.Replace(sheetRelationshipsDrawingVML, "..", "xl", -1)
|
||||
} else {
|
||||
// Add first comment for given sheet.
|
||||
rID := f.addSheetRelationships(sheet, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
|
||||
f.addSheetRelationships(sheet, SourceRelationshipComments, sheetRelationshipsComments, "")
|
||||
f.addSheetLegacyDrawing(sheet, rID)
|
||||
}
|
||||
commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
|
||||
f.addComment(commentsXML, cell, formatSet)
|
||||
var colCount int
|
||||
for i, l := range strings.Split(formatSet.Text, "\n") {
|
||||
if ll := len(l); ll > colCount {
|
||||
if i == 0 {
|
||||
ll += len(formatSet.Author)
|
||||
}
|
||||
colCount = ll
|
||||
}
|
||||
}
|
||||
f.addDrawingVML(commentID, drawingVML, cell, strings.Count(formatSet.Text, "\n")+1, colCount)
|
||||
f.addContentTypePart(commentID, "comments")
|
||||
return err
|
||||
}
|
||||
|
||||
// addDrawingVML provides a function to create comment as
|
||||
// xl/drawings/vmlDrawing%d.vml by given commit ID and cell.
|
||||
func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount, colCount int) {
|
||||
col := string(strings.Map(letterOnlyMapF, cell))
|
||||
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
|
||||
xAxis := row - 1
|
||||
yAxis := TitleToNumber(col)
|
||||
vml := vmlDrawing{
|
||||
XMLNSv: "urn:schemas-microsoft-com:vml",
|
||||
XMLNSo: "urn:schemas-microsoft-com:office:office",
|
||||
XMLNSx: "urn:schemas-microsoft-com:office:excel",
|
||||
XMLNSmv: "http://macVmlSchemaUri",
|
||||
Shapelayout: &xlsxShapelayout{
|
||||
Ext: "edit",
|
||||
IDmap: &xlsxIDmap{
|
||||
Ext: "edit",
|
||||
Data: commentID,
|
||||
},
|
||||
},
|
||||
Shapetype: &xlsxShapetype{
|
||||
ID: "_x0000_t202",
|
||||
Coordsize: "21600,21600",
|
||||
Spt: 202,
|
||||
Path: "m0,0l0,21600,21600,21600,21600,0xe",
|
||||
Stroke: &xlsxStroke{
|
||||
Joinstyle: "miter",
|
||||
},
|
||||
VPath: &vPath{
|
||||
Gradientshapeok: "t",
|
||||
Connecttype: "miter",
|
||||
},
|
||||
},
|
||||
}
|
||||
sp := encodeShape{
|
||||
Fill: &vFill{
|
||||
Color2: "#fbfe82",
|
||||
Angle: -180,
|
||||
Type: "gradient",
|
||||
Fill: &oFill{
|
||||
Ext: "view",
|
||||
Type: "gradientUnscaled",
|
||||
},
|
||||
},
|
||||
Shadow: &vShadow{
|
||||
On: "t",
|
||||
Color: "black",
|
||||
Obscured: "t",
|
||||
},
|
||||
Path: &vPath{
|
||||
Connecttype: "none",
|
||||
},
|
||||
Textbox: &vTextbox{
|
||||
Style: "mso-direction-alt:auto",
|
||||
Div: &xlsxDiv{
|
||||
Style: "text-align:left",
|
||||
},
|
||||
},
|
||||
ClientData: &xClientData{
|
||||
ObjectType: "Note",
|
||||
Anchor: fmt.Sprintf(
|
||||
"%d, 23, %d, 0, %d, %d, %d, 5",
|
||||
1+yAxis, 1+xAxis, 2+yAxis+lineCount, colCount+yAxis, 2+xAxis+lineCount),
|
||||
AutoFill: "True",
|
||||
Row: xAxis,
|
||||
Column: yAxis,
|
||||
},
|
||||
}
|
||||
s, _ := xml.Marshal(sp)
|
||||
shape := xlsxShape{
|
||||
ID: "_x0000_s1025",
|
||||
Type: "#_x0000_t202",
|
||||
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
|
||||
Fillcolor: "#fbf6d6",
|
||||
Strokecolor: "#edeaa1",
|
||||
Val: string(s[13 : len(s)-14]),
|
||||
}
|
||||
c, ok := f.XLSX[drawingVML]
|
||||
if ok {
|
||||
d := decodeVmlDrawing{}
|
||||
_ = xml.Unmarshal(namespaceStrictToTransitional(c), &d)
|
||||
for _, v := range d.Shape {
|
||||
s := xlsxShape{
|
||||
ID: "_x0000_s1025",
|
||||
Type: "#_x0000_t202",
|
||||
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
|
||||
Fillcolor: "#fbf6d6",
|
||||
Strokecolor: "#edeaa1",
|
||||
Val: v.Val,
|
||||
}
|
||||
vml.Shape = append(vml.Shape, s)
|
||||
}
|
||||
}
|
||||
vml.Shape = append(vml.Shape, shape)
|
||||
v, _ := xml.Marshal(vml)
|
||||
f.XLSX[drawingVML] = v
|
||||
}
|
||||
|
||||
// addComment provides a function to create chart as xl/comments%d.xml by
|
||||
// given cell and format sets.
|
||||
func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
|
||||
a := formatSet.Author
|
||||
t := formatSet.Text
|
||||
if len(a) > 255 {
|
||||
a = a[0:255]
|
||||
}
|
||||
if len(t) > 32512 {
|
||||
t = t[0:32512]
|
||||
}
|
||||
comments := xlsxComments{
|
||||
Authors: []xlsxAuthor{
|
||||
{
|
||||
Author: formatSet.Author,
|
||||
},
|
||||
},
|
||||
}
|
||||
cmt := xlsxComment{
|
||||
Ref: cell,
|
||||
AuthorID: 0,
|
||||
Text: xlsxText{
|
||||
R: []xlsxR{
|
||||
{
|
||||
RPr: &xlsxRPr{
|
||||
B: " ",
|
||||
Sz: &attrValFloat{Val: 9},
|
||||
Color: &xlsxColor{
|
||||
Indexed: 81,
|
||||
},
|
||||
RFont: &attrValString{Val: "Calibri"},
|
||||
Family: &attrValInt{Val: 2},
|
||||
},
|
||||
T: a,
|
||||
},
|
||||
{
|
||||
RPr: &xlsxRPr{
|
||||
Sz: &attrValFloat{Val: 9},
|
||||
Color: &xlsxColor{
|
||||
Indexed: 81,
|
||||
},
|
||||
RFont: &attrValString{Val: "Calibri"},
|
||||
Family: &attrValInt{Val: 2},
|
||||
},
|
||||
T: t,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
c, ok := f.XLSX[commentsXML]
|
||||
if ok {
|
||||
d := xlsxComments{}
|
||||
_ = xml.Unmarshal(namespaceStrictToTransitional(c), &d)
|
||||
comments.CommentList.Comment = append(comments.CommentList.Comment, d.CommentList.Comment...)
|
||||
}
|
||||
comments.CommentList.Comment = append(comments.CommentList.Comment, cmt)
|
||||
v, _ := xml.Marshal(comments)
|
||||
f.saveFileList(commentsXML, v)
|
||||
}
|
||||
|
||||
// countComments provides a function to get comments files count storage in
|
||||
// the folder xl.
|
||||
func (f *File) countComments() int {
|
||||
count := 0
|
||||
for k := range f.XLSX {
|
||||
if strings.Contains(k, "xl/comments") {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DataValidationType defined the type of data validation.
|
||||
type DataValidationType int
|
||||
|
||||
// Data validation types.
|
||||
const (
|
||||
_DataValidationType = iota
|
||||
typeNone // inline use
|
||||
DataValidationTypeCustom
|
||||
DataValidationTypeDate
|
||||
DataValidationTypeDecimal
|
||||
typeList // inline use
|
||||
DataValidationTypeTextLeng
|
||||
DataValidationTypeTime
|
||||
// DataValidationTypeWhole Integer
|
||||
DataValidationTypeWhole
|
||||
)
|
||||
|
||||
const (
|
||||
// dataValidationFormulaStrLen 255 characters+ 2 quotes
|
||||
dataValidationFormulaStrLen = 257
|
||||
// dataValidationFormulaStrLenErr
|
||||
dataValidationFormulaStrLenErr = "data validation must be 0-255 characters"
|
||||
)
|
||||
|
||||
// DataValidationErrorStyle defined the style of data validation error alert.
|
||||
type DataValidationErrorStyle int
|
||||
|
||||
// Data validation error styles.
|
||||
const (
|
||||
_ DataValidationErrorStyle = iota
|
||||
DataValidationErrorStyleStop
|
||||
DataValidationErrorStyleWarning
|
||||
DataValidationErrorStyleInformation
|
||||
)
|
||||
|
||||
// Data validation error styles.
|
||||
const (
|
||||
styleStop = "stop"
|
||||
styleWarning = "warning"
|
||||
styleInformation = "information"
|
||||
)
|
||||
|
||||
// DataValidationOperator operator enum.
|
||||
type DataValidationOperator int
|
||||
|
||||
// Data validation operators.
|
||||
const (
|
||||
_DataValidationOperator = iota
|
||||
DataValidationOperatorBetween
|
||||
DataValidationOperatorEqual
|
||||
DataValidationOperatorGreaterThan
|
||||
DataValidationOperatorGreaterThanOrEqual
|
||||
DataValidationOperatorLessThan
|
||||
DataValidationOperatorLessThanOrEqual
|
||||
DataValidationOperatorNotBetween
|
||||
DataValidationOperatorNotEqual
|
||||
)
|
||||
|
||||
// NewDataValidation return data validation struct.
|
||||
func NewDataValidation(allowBlank bool) *DataValidation {
|
||||
return &DataValidation{
|
||||
AllowBlank: allowBlank,
|
||||
ShowErrorMessage: false,
|
||||
ShowInputMessage: false,
|
||||
}
|
||||
}
|
||||
|
||||
// SetError set error notice.
|
||||
func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) {
|
||||
dd.Error = &msg
|
||||
dd.ErrorTitle = &title
|
||||
strStyle := styleStop
|
||||
switch style {
|
||||
case DataValidationErrorStyleStop:
|
||||
strStyle = styleStop
|
||||
case DataValidationErrorStyleWarning:
|
||||
strStyle = styleWarning
|
||||
case DataValidationErrorStyleInformation:
|
||||
strStyle = styleInformation
|
||||
|
||||
}
|
||||
dd.ShowErrorMessage = true
|
||||
dd.ErrorStyle = &strStyle
|
||||
}
|
||||
|
||||
// SetInput set prompt notice.
|
||||
func (dd *DataValidation) SetInput(title, msg string) {
|
||||
dd.ShowInputMessage = true
|
||||
dd.PromptTitle = &title
|
||||
dd.Prompt = &msg
|
||||
}
|
||||
|
||||
// SetDropList data validation list.
|
||||
func (dd *DataValidation) SetDropList(keys []string) error {
|
||||
dd.Formula1 = "\"" + strings.Join(keys, ",") + "\""
|
||||
dd.Type = convDataValidationType(typeList)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetRange provides function to set data validation range in drop list.
|
||||
func (dd *DataValidation) SetRange(f1, f2 int, t DataValidationType, o DataValidationOperator) error {
|
||||
formula1 := fmt.Sprintf("%d", f1)
|
||||
formula2 := fmt.Sprintf("%d", f2)
|
||||
if dataValidationFormulaStrLen < len(dd.Formula1) || dataValidationFormulaStrLen < len(dd.Formula2) {
|
||||
return fmt.Errorf(dataValidationFormulaStrLenErr)
|
||||
}
|
||||
|
||||
dd.Formula1 = formula1
|
||||
dd.Formula2 = formula2
|
||||
dd.Type = convDataValidationType(t)
|
||||
dd.Operator = convDataValidationOperatior(o)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetSqrefDropList provides set data validation on a range with source
|
||||
// reference range of the worksheet by given data validation object and
|
||||
// worksheet name. The data validation object can be created by
|
||||
// NewDataValidation function. For example, set data validation on
|
||||
// Sheet1!A7:B8 with validation criteria source Sheet1!E1:E3 settings, create
|
||||
// in-cell dropdown by allowing list source:
|
||||
//
|
||||
// dvRange := excelize.NewDataValidation(true)
|
||||
// dvRange.Sqref = "A7:B8"
|
||||
// dvRange.SetSqrefDropList("E1:E3", true)
|
||||
// xlsx.AddDataValidation("Sheet1", dvRange)
|
||||
//
|
||||
func (dd *DataValidation) SetSqrefDropList(sqref string, isCurrentSheet bool) error {
|
||||
if isCurrentSheet {
|
||||
dd.Formula1 = sqref
|
||||
dd.Type = convDataValidationType(typeList)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("cross-sheet sqref cell are not supported")
|
||||
}
|
||||
|
||||
// SetSqref provides function to set data validation range in drop list.
|
||||
func (dd *DataValidation) SetSqref(sqref string) {
|
||||
if dd.Sqref == "" {
|
||||
dd.Sqref = sqref
|
||||
} else {
|
||||
dd.Sqref = fmt.Sprintf("%s %s", dd.Sqref, sqref)
|
||||
}
|
||||
}
|
||||
|
||||
// convDataValidationType get excel data validation type.
|
||||
func convDataValidationType(t DataValidationType) string {
|
||||
typeMap := map[DataValidationType]string{
|
||||
typeNone: "none",
|
||||
DataValidationTypeCustom: "custom",
|
||||
DataValidationTypeDate: "date",
|
||||
DataValidationTypeDecimal: "decimal",
|
||||
typeList: "list",
|
||||
DataValidationTypeTextLeng: "textLength",
|
||||
DataValidationTypeTime: "time",
|
||||
DataValidationTypeWhole: "whole",
|
||||
}
|
||||
|
||||
return typeMap[t]
|
||||
|
||||
}
|
||||
|
||||
// convDataValidationOperatior get excel data validation operator.
|
||||
func convDataValidationOperatior(o DataValidationOperator) string {
|
||||
typeMap := map[DataValidationOperator]string{
|
||||
DataValidationOperatorBetween: "between",
|
||||
DataValidationOperatorEqual: "equal",
|
||||
DataValidationOperatorGreaterThan: "greaterThan",
|
||||
DataValidationOperatorGreaterThanOrEqual: "greaterThanOrEqual",
|
||||
DataValidationOperatorLessThan: "lessThan",
|
||||
DataValidationOperatorLessThanOrEqual: "lessThanOrEqual",
|
||||
DataValidationOperatorNotBetween: "notBetween",
|
||||
DataValidationOperatorNotEqual: "notEqual",
|
||||
}
|
||||
|
||||
return typeMap[o]
|
||||
|
||||
}
|
||||
|
||||
// AddDataValidation provides set data validation on a range of the worksheet
|
||||
// by given data validation object and worksheet name. The data validation
|
||||
// object can be created by NewDataValidation function.
|
||||
//
|
||||
// Example 1, set data validation on Sheet1!A1:B2 with validation criteria
|
||||
// settings, show error alert after invalid data is entered with "Stop" style
|
||||
// and custom title "error body":
|
||||
//
|
||||
// dvRange := excelize.NewDataValidation(true)
|
||||
// dvRange.Sqref = "A1:B2"
|
||||
// dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorBetween)
|
||||
// dvRange.SetError(excelize.DataValidationErrorStyleStop, "error title", "error body")
|
||||
// xlsx.AddDataValidation("Sheet1", dvRange)
|
||||
//
|
||||
// Example 2, set data validation on Sheet1!A3:B4 with validation criteria
|
||||
// settings, and show input message when cell is selected:
|
||||
//
|
||||
// dvRange = excelize.NewDataValidation(true)
|
||||
// dvRange.Sqref = "A3:B4"
|
||||
// dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorGreaterThan)
|
||||
// dvRange.SetInput("input title", "input body")
|
||||
// xlsx.AddDataValidation("Sheet1", dvRange)
|
||||
//
|
||||
// Example 3, set data validation on Sheet1!A5:B6 with validation criteria
|
||||
// settings, create in-cell dropdown by allowing list source:
|
||||
//
|
||||
// dvRange = excelize.NewDataValidation(true)
|
||||
// dvRange.Sqref = "A5:B6"
|
||||
// dvRange.SetDropList([]string{"1", "2", "3"})
|
||||
// xlsx.AddDataValidation("Sheet1", dvRange)
|
||||
//
|
||||
func (f *File) AddDataValidation(sheet string, dv *DataValidation) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
if nil == xlsx.DataValidations {
|
||||
xlsx.DataValidations = new(xlsxDataValidations)
|
||||
}
|
||||
xlsx.DataValidations.DataValidation = append(xlsx.DataValidations.DataValidation, dv)
|
||||
xlsx.DataValidations.Count = len(xlsx.DataValidations.DataValidation)
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// timeLocationUTC defined the UTC time location.
|
||||
var timeLocationUTC, _ = time.LoadLocation("UTC")
|
||||
|
||||
// timeToUTCTime provides a function to convert time to UTC time.
|
||||
func timeToUTCTime(t time.Time) time.Time {
|
||||
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), timeLocationUTC)
|
||||
}
|
||||
|
||||
// timeToExcelTime provides a function to convert time to Excel time.
|
||||
func timeToExcelTime(t time.Time) float64 {
|
||||
// TODO in future this should probably also handle date1904 and like TimeFromExcelTime
|
||||
var excelTime float64
|
||||
var deltaDays int64
|
||||
excelTime = 0
|
||||
deltaDays = 290 * 364
|
||||
// check if UnixNano would be out of int64 range
|
||||
for t.Unix() > deltaDays*24*60*60 {
|
||||
// reduce by aprox. 290 years, which is max for int64 nanoseconds
|
||||
delta := time.Duration(deltaDays) * 24 * time.Hour
|
||||
excelTime = excelTime + float64(deltaDays)
|
||||
t = t.Add(-delta)
|
||||
}
|
||||
// finally add remainder of UnixNano to keep nano precision
|
||||
// and 25569 which is days between 1900 and 1970
|
||||
return excelTime + float64(t.UnixNano())/8.64e13 + 25569.0
|
||||
}
|
||||
|
||||
// shiftJulianToNoon provides a function to process julian date to noon.
|
||||
func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
|
||||
switch {
|
||||
case -0.5 < julianFraction && julianFraction < 0.5:
|
||||
julianFraction += 0.5
|
||||
case julianFraction >= 0.5:
|
||||
julianDays++
|
||||
julianFraction -= 0.5
|
||||
case julianFraction <= -0.5:
|
||||
julianDays--
|
||||
julianFraction += 1.5
|
||||
}
|
||||
return julianDays, julianFraction
|
||||
}
|
||||
|
||||
// fractionOfADay provides a function to return the integer values for hour,
|
||||
// minutes, seconds and nanoseconds that comprised a given fraction of a day.
|
||||
// values would round to 1 us.
|
||||
func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {
|
||||
|
||||
const (
|
||||
c1us = 1e3
|
||||
c1s = 1e9
|
||||
c1day = 24 * 60 * 60 * c1s
|
||||
)
|
||||
|
||||
frac := int64(c1day*fraction + c1us/2)
|
||||
nanoseconds = int((frac%c1s)/c1us) * c1us
|
||||
frac /= c1s
|
||||
seconds = int(frac % 60)
|
||||
frac /= 60
|
||||
minutes = int(frac % 60)
|
||||
hours = int(frac / 60)
|
||||
return
|
||||
}
|
||||
|
||||
// julianDateToGregorianTime provides a function to convert julian date to
|
||||
// gregorian time.
|
||||
func julianDateToGregorianTime(part1, part2 float64) time.Time {
|
||||
part1I, part1F := math.Modf(part1)
|
||||
part2I, part2F := math.Modf(part2)
|
||||
julianDays := part1I + part2I
|
||||
julianFraction := part1F + part2F
|
||||
julianDays, julianFraction = shiftJulianToNoon(julianDays, julianFraction)
|
||||
day, month, year := doTheFliegelAndVanFlandernAlgorithm(int(julianDays))
|
||||
hours, minutes, seconds, nanoseconds := fractionOfADay(julianFraction)
|
||||
return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)
|
||||
}
|
||||
|
||||
// doTheFliegelAndVanFlandernAlgorithm; By this point generations of
|
||||
// programmers have repeated the algorithm sent to the editor of
|
||||
// "Communications of the ACM" in 1968 (published in CACM, volume 11, number
|
||||
// 10, October 1968, p.657). None of those programmers seems to have found it
|
||||
// necessary to explain the constants or variable names set out by Henry F.
|
||||
// Fliegel and Thomas C. Van Flandern. Maybe one day I'll buy that jounal and
|
||||
// expand an explanation here - that day is not today.
|
||||
func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
|
||||
l := jd + 68569
|
||||
n := (4 * l) / 146097
|
||||
l = l - (146097*n+3)/4
|
||||
i := (4000 * (l + 1)) / 1461001
|
||||
l = l - (1461*i)/4 + 31
|
||||
j := (80 * l) / 2447
|
||||
d := l - (2447*j)/80
|
||||
l = j / 11
|
||||
m := j + 2 - (12 * l)
|
||||
y := 100*(n-49) + i + l
|
||||
return d, m, y
|
||||
}
|
||||
|
||||
// timeFromExcelTime provides a function to convert an excelTime
|
||||
// representation (stored as a floating point number) to a time.Time.
|
||||
func timeFromExcelTime(excelTime float64, date1904 bool) time.Time {
|
||||
const MDD int64 = 106750 // Max time.Duration Days, aprox. 290 years
|
||||
var date time.Time
|
||||
var intPart = int64(excelTime)
|
||||
// Excel uses Julian dates prior to March 1st 1900, and Gregorian
|
||||
// thereafter.
|
||||
if intPart <= 61 {
|
||||
const OFFSET1900 = 15018.0
|
||||
const OFFSET1904 = 16480.0
|
||||
const MJD0 float64 = 2400000.5
|
||||
var date time.Time
|
||||
if date1904 {
|
||||
date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1904)
|
||||
} else {
|
||||
date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1900)
|
||||
}
|
||||
return date
|
||||
}
|
||||
var floatPart = excelTime - float64(intPart)
|
||||
var dayNanoSeconds float64 = 24 * 60 * 60 * 1000 * 1000 * 1000
|
||||
if date1904 {
|
||||
date = time.Date(1904, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
} else {
|
||||
date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
// Duration is limited to aprox. 290 years
|
||||
for intPart > MDD {
|
||||
durationDays := time.Duration(MDD) * time.Hour * 24
|
||||
date = date.Add(durationDays)
|
||||
intPart = intPart - MDD
|
||||
}
|
||||
durationDays := time.Duration(intPart) * time.Hour * 24
|
||||
durationPart := time.Duration(dayNanoSeconds * floatPart)
|
||||
return date.Add(durationDays).Add(durationPart)
|
||||
}
|
||||
+456
@@ -0,0 +1,456 @@
|
||||
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||
//
|
||||
// See https://xuri.me/excelize for more information about this package.
|
||||
package excelize
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// File define a populated XLSX file struct.
|
||||
type File struct {
|
||||
checked map[string]bool
|
||||
sheetMap map[string]string
|
||||
ContentTypes *xlsxTypes
|
||||
Path string
|
||||
SharedStrings *xlsxSST
|
||||
Sheet map[string]*xlsxWorksheet
|
||||
SheetCount int
|
||||
Styles *xlsxStyleSheet
|
||||
Theme *xlsxTheme
|
||||
WorkBook *xlsxWorkbook
|
||||
WorkBookRels *xlsxWorkbookRels
|
||||
XLSX map[string][]byte
|
||||
}
|
||||
|
||||
// OpenFile take the name of an XLSX file and returns a populated XLSX file
|
||||
// struct for it.
|
||||
func OpenFile(filename string) (*File, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
f, err := OpenReader(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Path = filename
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// OpenReader take an io.Reader and return a populated XLSX file.
|
||||
func OpenReader(r io.Reader) (*File, error) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, sheetCount, err := ReadZipReader(zr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f := &File{
|
||||
checked: make(map[string]bool),
|
||||
Sheet: make(map[string]*xlsxWorksheet),
|
||||
SheetCount: sheetCount,
|
||||
XLSX: file,
|
||||
}
|
||||
f.sheetMap = f.getSheetMap()
|
||||
f.Styles = f.stylesReader()
|
||||
f.Theme = f.themeReader()
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// setDefaultTimeStyle provides a function to set default numbers format for
|
||||
// time.Time type cell value by given worksheet name, cell coordinates and
|
||||
// number format code.
|
||||
func (f *File) setDefaultTimeStyle(sheet, axis string, format int) {
|
||||
if f.GetCellStyle(sheet, axis) == 0 {
|
||||
style, _ := f.NewStyle(`{"number_format": ` + strconv.Itoa(format) + `}`)
|
||||
f.SetCellStyle(sheet, axis, axis, style)
|
||||
}
|
||||
}
|
||||
|
||||
// workSheetReader provides a function to get the pointer to the structure
|
||||
// after deserialization by given worksheet name.
|
||||
func (f *File) workSheetReader(sheet string) *xlsxWorksheet {
|
||||
name, ok := f.sheetMap[trimSheetName(sheet)]
|
||||
if !ok {
|
||||
name = "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
|
||||
}
|
||||
if f.Sheet[name] == nil {
|
||||
var xlsx xlsxWorksheet
|
||||
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(name)), &xlsx)
|
||||
if f.checked == nil {
|
||||
f.checked = make(map[string]bool)
|
||||
}
|
||||
ok := f.checked[name]
|
||||
if !ok {
|
||||
checkSheet(&xlsx)
|
||||
checkRow(&xlsx)
|
||||
f.checked[name] = true
|
||||
}
|
||||
f.Sheet[name] = &xlsx
|
||||
}
|
||||
return f.Sheet[name]
|
||||
}
|
||||
|
||||
// checkSheet provides a function to fill each row element and make that is
|
||||
// continuous in a worksheet of XML.
|
||||
func checkSheet(xlsx *xlsxWorksheet) {
|
||||
row := len(xlsx.SheetData.Row)
|
||||
if row >= 1 {
|
||||
lastRow := xlsx.SheetData.Row[row-1].R
|
||||
if lastRow >= row {
|
||||
row = lastRow
|
||||
}
|
||||
}
|
||||
sheetData := xlsxSheetData{}
|
||||
existsRows := map[int]int{}
|
||||
for k := range xlsx.SheetData.Row {
|
||||
existsRows[xlsx.SheetData.Row[k].R] = k
|
||||
}
|
||||
for i := 0; i < row; i++ {
|
||||
_, ok := existsRows[i+1]
|
||||
if ok {
|
||||
sheetData.Row = append(sheetData.Row, xlsx.SheetData.Row[existsRows[i+1]])
|
||||
} else {
|
||||
sheetData.Row = append(sheetData.Row, xlsxRow{
|
||||
R: i + 1,
|
||||
})
|
||||
}
|
||||
}
|
||||
xlsx.SheetData = sheetData
|
||||
}
|
||||
|
||||
// replaceWorkSheetsRelationshipsNameSpaceBytes provides a function to replace
|
||||
// xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Microsoft
|
||||
// Office Excel 2007.
|
||||
func replaceWorkSheetsRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte {
|
||||
var oldXmlns = []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
||||
var newXmlns = []byte(`<worksheet xr:uid="{00000000-0001-0000-0000-000000000000}" xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac xr xr2 xr3" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mx="http://schemas.microsoft.com/office/mac/excel/2008/main" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
||||
workbookMarshal = bytes.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
|
||||
return workbookMarshal
|
||||
}
|
||||
|
||||
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
|
||||
// Office Excel 2007 and 2010. This function will be remove value tag when met a
|
||||
// cell have a linked value. Reference
|
||||
// https://social.technet.microsoft.com/Forums/office/en-US/e16bae1f-6a2c-4325-8013-e989a3479066/excel-2010-linked-cells-not-updating?forum=excel
|
||||
//
|
||||
// Notice: after open XLSX file Excel will be update linked value and generate
|
||||
// new value and will prompt save file or not.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// <row r="19" spans="2:2">
|
||||
// <c r="B19">
|
||||
// <f>SUM(Sheet2!D2,Sheet2!D11)</f>
|
||||
// <v>100</v>
|
||||
// </c>
|
||||
// </row>
|
||||
//
|
||||
// to
|
||||
//
|
||||
// <row r="19" spans="2:2">
|
||||
// <c r="B19">
|
||||
// <f>SUM(Sheet2!D2,Sheet2!D11)</f>
|
||||
// </c>
|
||||
// </row>
|
||||
//
|
||||
func (f *File) UpdateLinkedValue() {
|
||||
for _, name := range f.GetSheetMap() {
|
||||
xlsx := f.workSheetReader(name)
|
||||
for indexR := range xlsx.SheetData.Row {
|
||||
for indexC, col := range xlsx.SheetData.Row[indexR].C {
|
||||
if col.F != nil && col.V != "" {
|
||||
xlsx.SheetData.Row[indexR].C[indexC].V = ""
|
||||
xlsx.SheetData.Row[indexR].C[indexC].T = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjustHelper provides a function to adjust rows and columns dimensions,
|
||||
// hyperlinks, merged cells and auto filter when inserting or deleting rows or
|
||||
// columns.
|
||||
//
|
||||
// sheet: Worksheet name that we're editing
|
||||
// column: Index number of the column we're inserting/deleting before
|
||||
// row: Index number of the row we're inserting/deleting before
|
||||
// offset: Number of rows/column to insert/delete negative values indicate deletion
|
||||
//
|
||||
// TODO: adjustPageBreaks, adjustComments, adjustDataValidations, adjustProtectedCells
|
||||
//
|
||||
func (f *File) adjustHelper(sheet string, column, row, offset int) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
f.adjustRowDimensions(xlsx, row, offset)
|
||||
f.adjustColDimensions(xlsx, column, offset)
|
||||
f.adjustHyperlinks(sheet, column, row, offset)
|
||||
f.adjustMergeCells(xlsx, column, row, offset)
|
||||
f.adjustAutoFilter(xlsx, column, row, offset)
|
||||
checkSheet(xlsx)
|
||||
checkRow(xlsx)
|
||||
}
|
||||
|
||||
// adjustColDimensions provides a function to update column dimensions when
|
||||
// inserting or deleting rows or columns.
|
||||
func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, column, offset int) {
|
||||
for i, r := range xlsx.SheetData.Row {
|
||||
for k, v := range r.C {
|
||||
axis := v.R
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
yAxis := TitleToNumber(col)
|
||||
if yAxis >= column && column != -1 {
|
||||
xlsx.SheetData.Row[i].C[k].R = ToAlphaString(yAxis+offset) + strconv.Itoa(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjustRowDimensions provides a function to update row dimensions when
|
||||
// inserting or deleting rows or columns.
|
||||
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, rowIndex, offset int) {
|
||||
if rowIndex == -1 {
|
||||
return
|
||||
}
|
||||
for i, r := range xlsx.SheetData.Row {
|
||||
if r.R >= rowIndex {
|
||||
f.ajustSingleRowDimensions(&xlsx.SheetData.Row[i], offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ajustSingleRowDimensions provides a function to ajust single row
|
||||
// dimensions.
|
||||
func (f *File) ajustSingleRowDimensions(r *xlsxRow, offset int) {
|
||||
r.R += offset
|
||||
for i, col := range r.C {
|
||||
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, col.R))
|
||||
r.C[i].R = string(strings.Map(letterOnlyMapF, col.R)) + strconv.Itoa(row+offset)
|
||||
}
|
||||
}
|
||||
|
||||
// adjustHyperlinks provides a function to update hyperlinks when inserting or
|
||||
// deleting rows or columns.
|
||||
func (f *File) adjustHyperlinks(sheet string, column, rowIndex, offset int) {
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
|
||||
// order is important
|
||||
if xlsx.Hyperlinks != nil && offset < 0 {
|
||||
for i, v := range xlsx.Hyperlinks.Hyperlink {
|
||||
axis := v.Ref
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
yAxis := TitleToNumber(col)
|
||||
if row == rowIndex || yAxis == column {
|
||||
f.deleteSheetRelationships(sheet, v.RID)
|
||||
if len(xlsx.Hyperlinks.Hyperlink) > 1 {
|
||||
xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink[:i], xlsx.Hyperlinks.Hyperlink[i+1:]...)
|
||||
} else {
|
||||
xlsx.Hyperlinks = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if xlsx.Hyperlinks != nil {
|
||||
for i, v := range xlsx.Hyperlinks.Hyperlink {
|
||||
axis := v.Ref
|
||||
col := string(strings.Map(letterOnlyMapF, axis))
|
||||
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||
xAxis := row + offset
|
||||
yAxis := TitleToNumber(col)
|
||||
if rowIndex != -1 && row >= rowIndex {
|
||||
xlsx.Hyperlinks.Hyperlink[i].Ref = col + strconv.Itoa(xAxis)
|
||||
}
|
||||
if column != -1 && yAxis >= column {
|
||||
xlsx.Hyperlinks.Hyperlink[i].Ref = ToAlphaString(yAxis+offset) + strconv.Itoa(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjustMergeCellsHelper provides a function to update merged cells when
|
||||
// inserting or deleting rows or columns.
|
||||
func (f *File) adjustMergeCellsHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||
if xlsx.MergeCells != nil {
|
||||
for k, v := range xlsx.MergeCells.Cells {
|
||||
beg := strings.Split(v.Ref, ":")[0]
|
||||
end := strings.Split(v.Ref, ":")[1]
|
||||
|
||||
begcol := string(strings.Map(letterOnlyMapF, beg))
|
||||
begrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, beg))
|
||||
begxAxis := begrow + offset
|
||||
begyAxis := TitleToNumber(begcol)
|
||||
|
||||
endcol := string(strings.Map(letterOnlyMapF, end))
|
||||
endrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, end))
|
||||
endxAxis := endrow + offset
|
||||
endyAxis := TitleToNumber(endcol)
|
||||
|
||||
if rowIndex != -1 {
|
||||
if begrow > 1 && begrow >= rowIndex {
|
||||
beg = begcol + strconv.Itoa(begxAxis)
|
||||
}
|
||||
if endrow > 1 && endrow >= rowIndex {
|
||||
end = endcol + strconv.Itoa(endxAxis)
|
||||
}
|
||||
}
|
||||
|
||||
if column != -1 {
|
||||
if begyAxis >= column {
|
||||
beg = ToAlphaString(begyAxis+offset) + strconv.Itoa(endrow)
|
||||
}
|
||||
if endyAxis >= column {
|
||||
end = ToAlphaString(endyAxis+offset) + strconv.Itoa(endrow)
|
||||
}
|
||||
}
|
||||
|
||||
xlsx.MergeCells.Cells[k].Ref = beg + ":" + end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjustMergeCells provides a function to update merged cells when inserting
|
||||
// or deleting rows or columns.
|
||||
func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||
f.adjustMergeCellsHelper(xlsx, column, rowIndex, offset)
|
||||
|
||||
if xlsx.MergeCells != nil && offset < 0 {
|
||||
for k, v := range xlsx.MergeCells.Cells {
|
||||
beg := strings.Split(v.Ref, ":")[0]
|
||||
end := strings.Split(v.Ref, ":")[1]
|
||||
if beg == end {
|
||||
xlsx.MergeCells.Count += offset
|
||||
if len(xlsx.MergeCells.Cells) > 1 {
|
||||
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:k], xlsx.MergeCells.Cells[k+1:]...)
|
||||
} else {
|
||||
xlsx.MergeCells = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjustAutoFilter provides a function to update the auto filter when
|
||||
// inserting or deleting rows or columns.
|
||||
func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||
f.adjustAutoFilterHelper(xlsx, column, rowIndex, offset)
|
||||
|
||||
if xlsx.AutoFilter != nil {
|
||||
beg := strings.Split(xlsx.AutoFilter.Ref, ":")[0]
|
||||
end := strings.Split(xlsx.AutoFilter.Ref, ":")[1]
|
||||
|
||||
begcol := string(strings.Map(letterOnlyMapF, beg))
|
||||
begrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, beg))
|
||||
begxAxis := begrow + offset
|
||||
|
||||
endcol := string(strings.Map(letterOnlyMapF, end))
|
||||
endrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, end))
|
||||
endxAxis := endrow + offset
|
||||
endyAxis := TitleToNumber(endcol)
|
||||
|
||||
if rowIndex != -1 {
|
||||
if begrow >= rowIndex {
|
||||
beg = begcol + strconv.Itoa(begxAxis)
|
||||
}
|
||||
if endrow >= rowIndex {
|
||||
end = endcol + strconv.Itoa(endxAxis)
|
||||
}
|
||||
}
|
||||
|
||||
if column != -1 && endyAxis >= column {
|
||||
end = ToAlphaString(endyAxis+offset) + strconv.Itoa(endrow)
|
||||
}
|
||||
xlsx.AutoFilter.Ref = beg + ":" + end
|
||||
}
|
||||
}
|
||||
|
||||
// adjustAutoFilterHelper provides a function to update the auto filter when
|
||||
// inserting or deleting rows or columns.
|
||||
func (f *File) adjustAutoFilterHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||
if xlsx.AutoFilter != nil {
|
||||
beg := strings.Split(xlsx.AutoFilter.Ref, ":")[0]
|
||||
end := strings.Split(xlsx.AutoFilter.Ref, ":")[1]
|
||||
|
||||
begcol := string(strings.Map(letterOnlyMapF, beg))
|
||||
begrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, beg))
|
||||
begyAxis := TitleToNumber(begcol)
|
||||
|
||||
endcol := string(strings.Map(letterOnlyMapF, end))
|
||||
endyAxis := TitleToNumber(endcol)
|
||||
endrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, end))
|
||||
|
||||
if (begrow == rowIndex && offset < 0) || (column == begyAxis && column == endyAxis) {
|
||||
xlsx.AutoFilter = nil
|
||||
for i, r := range xlsx.SheetData.Row {
|
||||
if begrow < r.R && r.R <= endrow {
|
||||
xlsx.SheetData.Row[i].Hidden = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetMergeCells provides a function to get all merged cells from a worksheet currently.
|
||||
func (f *File) GetMergeCells(sheet string) []MergeCell {
|
||||
mergeCells := []MergeCell{}
|
||||
|
||||
xlsx := f.workSheetReader(sheet)
|
||||
if xlsx.MergeCells != nil {
|
||||
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
|
||||
ref := xlsx.MergeCells.Cells[i].Ref
|
||||
axis := strings.Split(ref, ":")[0]
|
||||
mergeCells = append(mergeCells, []string{ref, f.GetCellValue(sheet, axis)})
|
||||
}
|
||||
}
|
||||
|
||||
return mergeCells
|
||||
}
|
||||
|
||||
// MergeCell define a merged cell data.
|
||||
// It consists of the following structure.
|
||||
// example: []string{"D4:E10", "cell value"}
|
||||
type MergeCell []string
|
||||
|
||||
// GetCellValue returns merged cell value.
|
||||
func (m *MergeCell) GetCellValue() string {
|
||||
return (*m)[1]
|
||||
}
|
||||
|
||||
// GetStartAxis returns the merge start axis.
|
||||
// example: "C2"
|
||||
func (m *MergeCell) GetStartAxis() string {
|
||||
axis := strings.Split((*m)[0], ":")
|
||||
return axis[0]
|
||||
}
|
||||
|
||||
// GetEndAxis returns the merge end axis.
|
||||
// example: "D4"
|
||||
func (m *MergeCell) GetEndAxis() string {
|
||||
axis := strings.Split((*m)[0], ":")
|
||||
return axis[1]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user