Browse Source

打包成exe 服务

master
娄文智 2 years ago
parent
commit
1f74da8b71
13 changed files with 198 additions and 90 deletions
  1. +56
    -6
      conf/config.go
  2. +47
    -16
      main.go
  3. +28
    -10
      task/func.go
  4. +11
    -6
      task/task.go
  5. +9
    -1
      utils/const.go
  6. +1
    -1
      web/controllers/lab_controller.go
  7. +14
    -11
      web/middleware/casbins/casbins.go
  8. +22
    -31
      web/models/buffer_model.go
  9. +3
    -1
      web/models/etltab_model.go
  10. +1
    -1
      web/models/me_attribute_model.go
  11. +1
    -1
      web/models/me_part_model.go
  12. +1
    -1
      web/models/me_partrule_model.go
  13. +4
    -4
      web/models/pln_forecast_item_dailydemand_model.go

+ 56
- 6
conf/config.go View File

@ -1,11 +1,13 @@
package conf
import (
"LAPP_SJA_ME/utils"
"github.com/spf13/viper"
"gopkg.in/yaml.v2"
"os"
)
var DbConfig *EnvConfig
var DbConfig EnvConfig
type EnvConfig struct {
DbType string `yaml:"dbtype"`
@ -18,6 +20,8 @@ type EnvConfig struct {
TemplatePath string `yaml:"templatepath"`
ReadTaskInterval int `yaml:"readtaskinterval"`
PrinterType string `yaml:"printertype"`
Printnum int `yaml:"printnum"`
Msgtype string `yaml:"msgtype"`
Finr int `yaml:"finr"`
Inbox string `yaml:"inbox"`
Outbox string `yaml:"outbox"`
@ -28,16 +32,62 @@ type EnvConfig struct {
//read yaml config
//注:path为yaml或yml文件的路径
func ReadYamlConfig(path string) (*EnvConfig, error) {
DbConfig := &EnvConfig{}
func ReadYamlConfig() error {
path, err := utils.GetCurrentPath("conf/config.yaml")
if err != nil {
return err
}
f, err := os.Open(path)
if err != nil {
return nil, err
return err
} else {
yaml.NewDecoder(f).Decode(DbConfig)
yaml.NewDecoder(f).Decode(&DbConfig)
}
defer f.Close()
return DbConfig, nil
return nil
}
var AppInfo Config
type Config struct {
*App `mapstructure:"app"`
*ETCD `mapstructure:"etcd"`
}
type App struct {
Port int `yaml:"port"`
TaskNums int `yaml:"tasknums"`
ErrNums int `yaml:"errnums"`
ShellPath string `yaml:"shellpath"`
LocalAddr string `yaml:"localaddr"`
Mod string `yaml:"mod"`
Name string `yaml:"name"`
UseETCD bool `yaml:"useetcd"`
}
type ETCD struct {
Addrs []string `yaml:"addrs"`
Timeout int `yaml:"timeout"`
LockLease int64 `yaml:"locklease"`
ServiceLease int64 `yaml:"servicelease"`
}
func InitConfig() (err error) {
baseDir, err := utils.GetCurrentPath("conf")
if err != nil {
return
}
viper.SetConfigName("app_config")
viper.SetConfigType("yaml")
viper.AddConfigPath(baseDir)
if err = viper.ReadInConfig(); err != nil {
return
}
if err = viper.Unmarshal(&AppInfo); err != nil {
return
}
return nil
}

+ 47
- 16
main.go View File

@ -3,7 +3,6 @@ package main
import (
"LAPP_SJA_ME/conf"
"LAPP_SJA_ME/db"
Engine "LAPP_SJA_ME/engine"
"LAPP_SJA_ME/etcd"
"LAPP_SJA_ME/inits/parse"
"LAPP_SJA_ME/lab"
@ -39,9 +38,9 @@ func (p *program) Stop(s service.Service) error {
func main() {
svcConfig := &service.Config{
Name: "LeitServer",
DisplayName: "LeitServer",
Description: "this is LeitServer",
Name: "G38_LAPP_ME_WEB",
DisplayName: "G38_LAPP_ME_WEB",
Description: "this is G38_LAPP_ME_WEB",
}
prg := &program{}
@ -54,11 +53,19 @@ func main() {
switch verb {
case "install":
s.Install()
log.Println("服务安装成功")
log.Println("Install success")
return
case "remove":
s.Uninstall()
log.Println("服务卸载成功")
log.Println("remove success")
return
case "start":
s.Start()
log.Println("start server")
return
case "stop":
s.Stop()
log.Println("stop server")
return
}
@ -79,7 +86,10 @@ func imain() {
defer glog.Flush()
fmt.Println(baseDir)
baseDir = utils.EnsureDir(baseDir)
baseDir,err := utils.GetCurrentPath(baseDir)
if err != nil {
glog.InfoExtln("baseDir err :", err)
}
fmt.Println(baseDir)
pathLogDir := filepath.Join(baseDir, "glog")
fmt.Println(pathLogDir)
@ -88,7 +98,7 @@ func imain() {
glog.Infoln("启动日志", "InitDB return success")
//初始化数据库
err := db.InitDb()
err = db.InitDb()
if err != nil {
glog.InfoExtln("数据库加载失败", err)
}
@ -96,7 +106,14 @@ func imain() {
parse.AppOtherParse()
//加载配置
conf, err := conf.ReadYamlConfig(conf.Confurl)
err = conf.InitConfig()
if err != nil {
log.Printf("failed to read yaml config due to: %v", err)
return
}
//加载配置
err = conf.ReadYamlConfig()
if err != nil {
log.Printf("failed to read yaml config due to: %v", err)
return
@ -111,19 +128,32 @@ func imain() {
app.RegisterView(iris.HTML("./web/public", ".html"))
// 设置静态资源
app.StaticWeb("/public", "./web/public")
//注册服务
//go etcd.Etcdrun()
//检测
//go func() {
// http.ListenAndServe("0.0.0.0:8899", nil)
//}()
//创建etcd连接
if err = etcd.InitJobLock(); err != nil {
glog.Infoln("初始化EtCD连接", "InitJobLock return fail")
return
}
//创建etcd监听连接
if err = etcd.InitEtcdClient(); err != nil {
glog.Infoln("初始化EtCD连接", "InitJobLock return fail")
return
}
// register app service to etcd
if conf.AppInfo.UseETCD {
CheckServiceDao := etcd.NewETCDServiceImplement(etcd.AppEtcdClient, conf.AppInfo.App.LocalAddr)
defer CheckServiceDao.RevokeLease()
err = CheckServiceDao.RegisterService(conf.AppInfo.App.Name)
if err != nil {
log.Fatal("register service to etcd failed, error:", err)
}
}
//打印服务
go Engine.RunEngine(conf)
//go Engine.RunEngine(config)
//开启etl任务
go task.CreateCacheTask()
@ -133,5 +163,6 @@ func imain() {
//启动监听端口
app.Run(iris.Addr(":8090"), iris.WithConfiguration(parse.C))
//app.Run(iris.Addr(":9003"), iris.WithConfiguration(parse.C))
}

+ 28
- 10
task/func.go View File

@ -4,6 +4,7 @@ import (
"LAPP_SJA_ME/db"
"LAPP_SJA_ME/etcd"
"LAPP_SJA_ME/utils"
"LAPP_SJA_ME/web/middleware/glog"
"LAPP_SJA_ME/web/models"
"fmt"
"time"
@ -24,7 +25,7 @@ LOOP:
err := etcd.G_jobLock.TryLock("lock")
if err != nil {
fmt.Println("pln_workorder groutine lock fail!")
time.Sleep(5 * time.Millisecond)
time.Sleep(2 * time.Millisecond)
goto LOOP
}
fmt.Println("pln_workorder groutine lock success!")
@ -38,35 +39,49 @@ LOOP:
//判断传入状态
switch status {
case 40:
//更新生产订单表
_, err := session.Table("pln_workorder").Cols("status").Where("finr = ? and workordernr = ? and status <= ?", finr, workordernr, status).Update(&map[string]interface{}{"status": 40})
glog.InfoExtln("UpdatePlnWorkorder", "workordernr:", workordernr, status, optime)
//查询生产订单对应的客户订单是否有小于80状态的订单
var pln models.Pln_workorder
_, err = session.Table("pln_workorder").Where("finr = ? and workordernr = ?", finr, workordernr).Get(&pln)
if err != nil {
glog.InfoExtln("UpdatePlnWorkorder", "err:", err)
session.Rollback()
session.Close()
etcd.G_jobLock.UnLock()
return err
}
//查询生产订单对应的客户订单是否有小于80状态的订单
var pln models.Pln_workorder
_, err = session.Table("pln_workorder").Where("finr = ? and workordernr = ?", finr, workordernr).Get(&pln)
if pln.Status > 40 {
glog.InfoExtln("UpdatePlnWorkorder", "err:", "订单状态大于40")
session.Rollback()
session.Close()
etcd.G_jobLock.UnLock()
return nil
}
//更新生产订单表
_, err := session.Table("pln_workorder").Cols("status").Where("finr = ? and workordernr = ?", finr, workordernr).Update(&map[string]interface{}{"status": 40})
if err != nil {
glog.InfoExtln("UpdatePlnWorkorder", "err:", err)
session.Rollback()
session.Close()
etcd.G_jobLock.UnLock()
return err
}
//更新客户订单
_, err = session.Table("pln_custorder").Cols("status").Where("finr = ? and custordernr = ? and status <= ?", finr, pln.Custordernr, status).Update(&map[string]interface{}{"status": 40})
_, err = session.Table("pln_custorder").Cols("status").Where("finr = ? and custordernr = ? ", finr, pln.Custordernr).Update(&map[string]interface{}{"status": 40})
if err != nil {
glog.InfoExtln("UpdatePlnWorkorder", "err:", err)
session.Rollback()
session.Close()
etcd.G_jobLock.UnLock()
return err
}
case 80:
glog.InfoExtln("UpdatePlnWorkorder", "workordernr:", workordernr, status, optime)
//更新生产订单表
_, err := session.Table("pln_workorder").Cols("status", "schedbegtime").Where("finr = ? and workordernr = ? and status <= ?", finr, workordernr, status).Update(&map[string]interface{}{"status": 80, "schedbegtime": optime})
_, err := session.Table("pln_workorder").Cols("status", "schedbegtime").Where("finr = ? and workordernr = ?", finr, workordernr).Update(&map[string]interface{}{"status": 80, "schedbegtime": optime})
if err != nil {
glog.InfoExtln("UpdatePlnWorkorder", "err:", err)
session.Rollback()
session.Close()
etcd.G_jobLock.UnLock()
@ -76,6 +91,7 @@ LOOP:
var pln models.Pln_workorder
_, err = session.Table("pln_workorder").Where("finr = ? and workordernr = ?", finr, workordernr).Get(&pln)
if err != nil {
glog.InfoExtln("UpdatePlnWorkorder", "err:", err)
session.Rollback()
session.Close()
etcd.G_jobLock.UnLock()
@ -84,6 +100,7 @@ LOOP:
var plc models.Pln_workorder
ok, err := session.Table("pln_workorder").Where("finr = ? and custordernr = ? and status < ?", finr, pln.Custordernr, status).Get(&plc)
if err != nil {
glog.InfoExtln("UpdatePlnWorkorder", "err:", err)
session.Rollback()
session.Close()
etcd.G_jobLock.UnLock()
@ -93,6 +110,7 @@ LOOP:
//生产订单不存在小于80的状态,更新生产订单
_, err := session.Table("pln_custorder").Cols("status").Where("finr = ? and custordernr = ?", finr, pln.Custordernr).Update(&map[string]interface{}{"status": 80})
if err != nil {
glog.InfoExtln("UpdatePlnWorkorder", "err:", err)
session.Rollback()
session.Close()
etcd.G_jobLock.UnLock()
@ -114,13 +132,13 @@ func UpdatePlnBatchorder(finr int, batchordernr string, status int, actqty int,
switch status {
case 40:
//更新批次订单表
_, err := engine.Table("pln_batchorder").Cols("status", "actqty", "actbegtime").Where("finr = ? and batchordernr = ? and status <= ?", finr, batchordernr, status).Update(&map[string]interface{}{"status": 40, "actqty": actqty, "actbegtime": optime})
_, err := engine.Table("pln_batchorder").Cols("status", "actqty", "actbegtime").Where("finr = ? and batchordernr = ?", finr, batchordernr).Update(&map[string]interface{}{"status": 40, "actqty": actqty, "actbegtime": optime})
if err != nil {
return err
}
case 80:
//更新生产订单表
_, err := engine.Table("pln_batchorder").Cols("status", "actqty", "actendtime").Where("finr = ? and batchordernr = ? and status <= ?", finr, batchordernr, status).Update(&map[string]interface{}{"status": 80, "actqty": actqty, "actendtime": optime})
_, err := engine.Table("pln_batchorder").Cols("status", "actqty", "actendtime").Where("finr = ? and batchordernr = ?", finr, batchordernr).Update(&map[string]interface{}{"status": 80, "actqty": actqty, "actendtime": optime})
if err != nil {
return err
}


+ 11
- 6
task/task.go View File

@ -269,6 +269,7 @@ func ToLeadDataBase() {
time.Sleep(1 * time.Second)
continue
}
glog.InfoExtln("UpdatePlnWorkorder", "len(data):", len(data))
//第二步:导入从库,
for _, v := range data {
//判断执行次数
@ -280,22 +281,26 @@ func ToLeadDataBase() {
//json解析
err := json.Unmarshal([]byte(v.Data), &tems)
if err != nil {
glog.InfoExtln("etl导入从库", "Unmarshal err is :", err)
glog.InfoExtln("etl导入从库err", "Unmarshal err is :", err)
continue
}
switch v.Funcspec {
case "UpdatePlnWorkorder":
finr := v.Finr
workordernr := utils.ValueToString(tems["ordernr"], "")
glog.InfoExtln("UpdatePlnWorkorder", "workordernr task.go 295:", workordernr)
status := utils.ValueToInt(tems["status"], 0)
tem := utils.ValueToString(tems["optime"], "")
temtime, _ := time.Parse("2006-01-02T15:04:05Z07:00", tem)
temtime, err := time.Parse("2006-01-02T15:04:05Z07:00", tem)
if err != nil {
glog.InfoExtln("UpdatePlnWorkorder", "temtime err is :", err)
continue
}
optime := temtime.Format("20060102150405")
glog.InfoExtln("UpdatePlnWorkorder", "workordernr task.go 295:", workordernr)
err = UpdatePlnWorkorder(finr, workordernr, status, optime)
if err != nil {
glog.InfoExtln("etl导入从库", "UpdatePlnWorkorder err is :", err)
glog.InfoExtln("etl导入从库", "ordernr is :", workordernr)
glog.InfoExtln("etl导入从库", "ordernr is :", status)
glog.InfoExtln("UpdatePlnWorkorder", "UpdatePlnWorkorder err is :", err)
continue
}
//更新mongdb数据flag
@ -319,7 +324,7 @@ func ToLeadDataBase() {
optime := temtime.Format("20060102150405")
err = UpdatePlnBatchorder(finr, batchordernr, status, actqty, optime)
if err != nil {
glog.InfoExtln("etl导入从库", "UpdatePlnBatchorder err is :", err)
glog.InfoExtln("UpdatePlnBatchorder", "UpdatePlnBatchorder err is :", err)
continue
}
//更新mongdb数据flag


+ 9
- 1
utils/const.go View File

@ -18,4 +18,12 @@ package utils
*
******************************************************************************/
const Finr = 100
const Finr = 100
// ETCD z状态
const (
// 任务状态
SERVICE_STATUS_RUNNING = "RUNNING"
SERVICE_STATUS_STOP = "STOP"
SERVICE_STATUS_PAUSE = "PAUSE"
)

+ 1
- 1
web/controllers/lab_controller.go View File

@ -56,7 +56,7 @@ func UploadLab(ctx iris.Context) {
return
}
}
savePath = utils.EnsureDir(savePath)
savePath,_ = utils.GetCurrentPath(savePath)
// 初始化
lab = &models.LAB{}
if err = models.ParseLabEdi(savePath, lab); err != nil {


+ 14
- 11
web/middleware/casbins/casbins.go View File

@ -19,24 +19,27 @@ import (
// 获取Enforcer
func NewCasbin() (*casbin.Enforcer, error) {
c := conf.MasterDbConfig
driveSource := fmt.Sprintf("server=%s;database=%s;user id=%s;password=%s;port=%d;encrypt=disable",
c.Host, c.DbName, c.User, c.Pwd, c.Port)
conf.DbConfig.Server, conf.DbConfig.DbName, conf.DbConfig.User, conf.DbConfig.Password, conf.DbConfig.Port)
a, err := xormadapter.NewAdapter(conf.DriverName, driveSource, true)
if err != nil {
log.Printf("连接数据库错误: %v", err)
return nil,err
return nil, err
}
savePath, err := utils.GetCurrentPath("conf/rbac_model.conf")
if err != nil {
log.Printf("savePath路径错误: %v", err)
return nil, err
}
savePath := utils.EnsureDir("conf/rbac_model.conf")
e, err := casbin.NewEnforcer(savePath, a)
if err !=nil{
if err != nil {
//日志
logs := new(models.LeitServerLog)
logs.File = "/casbins.go"
logs.Level = "debug"
logs.Function = "CheckPermissions:NewCasbin()"
logs.Message ="conf/rbac_model.conf文件加载错误"
logs.Message = "conf/rbac_model.conf文件加载错误"
logs.Operator = "debug"
logs.TimeStamp = utils.TimeFormat(time.Now(), "yyyyMMddHHmmss")
logs.InsertRecord()
@ -65,19 +68,19 @@ func CheckPermissions(ctx context.Context) bool {
// 去除空格
//uid := strings.Replace(user.Userid, " ", "", -1)
uid := strings.TrimSpace(user.Role)
e,err:=NewCasbin()
if err !=nil {
e, err := NewCasbin()
if err != nil {
supports.Unauthorized(ctx, supports.PermissionsLess, nil)
ctx.StopExecution()
return false
}
ok,err = e.Enforce(uid, ctx.Path(), ctx.Method())
ok, err = e.Enforce(uid, ctx.Path(), ctx.Method())
log.Println("----------------", uid, ctx.Path(), ctx.Method())
log.Println(ok)
if err != nil{
if err != nil {
log.Printf("casbin权限控制错误: %v", err)
}
if !ok{
if !ok {
supports.Unauthorized(ctx, supports.PermissionsLess, nil)
ctx.StopExecution()
return false


+ 22
- 31
web/models/buffer_model.go View File

@ -121,7 +121,7 @@ func (t *Buffer) FindData() []*Buffer {
}, &options.FindOptions{
Skip: &skip,
Limit: &limit,
Sort: bson.D{{"timestamp", -1}},
Sort: bson.D{{"timestamp", 1}},
})
if err != nil {
return nil
@ -178,37 +178,28 @@ func (t *Buffer) UpdateDataTimes() error {
client := db.MgoDb()
//2.选择数据库,数据表
collect := client.Database(conf.MongDbConfig.DbName).Collection("buffer")
if t.Times > 5 {
// 删除
_, err := collect.DeleteOne(context.Background(), bson.M{"orderid": t.Orderid})
if err != nil {
return err
}
return nil
} else {
new := &Buffer{
Orderid: t.Orderid,
Eid: t.Eid,
Finr: t.Finr,
Data: t.Data,
TimeStamp: t.TimeStamp,
Todrivername: t.Todrivername,
Totable: t.Totable,
Todb: t.Todb,
Status: t.Status,
Message: t.Message,
Funcspec: t.Funcspec,
Dbtype: t.Dbtype,
Times: t.Times + 1,
Flag: 0,
}
update := bson.M{"$set": new}
_, err := collect.UpdateOne(context.Background(), bson.M{"orderid": new.Orderid}, update)
if err != nil {
return err
}
return nil
new := &Buffer{
Orderid: t.Orderid,
Eid: t.Eid,
Finr: t.Finr,
Data: t.Data,
TimeStamp: utils.TimeFormat(time.Now(), "yyyyMMddHHmmss"),
Todrivername: t.Todrivername,
Totable: t.Totable,
Todb: t.Todb,
Status: t.Status,
Message: t.Message,
Funcspec: t.Funcspec,
Dbtype: t.Dbtype,
Times: t.Times + 1,
Flag: 0,
}
update := bson.M{"$set": new}
_, err := collect.UpdateOne(context.Background(), bson.M{"orderid": new.Orderid}, update)
if err != nil {
return err
}
return nil
}


+ 3
- 1
web/models/etltab_model.go View File

@ -3,6 +3,7 @@ package models
import (
"LAPP_SJA_ME/db"
"LAPP_SJA_ME/utils"
"LAPP_SJA_ME/web/middleware/glog"
"errors"
"github.com/go-xorm/xorm"
"strings"
@ -221,6 +222,7 @@ func (t *Etltab) SelectArr() ([]Etltablst, error) {
func (t *Etltab) SelectAll() ([]Etltab, error) {
es := db.Eloquent.Slaves()
e := es[0]
e.ShowSQL(true)
data := make([]Etltab, 0)
err := e.Table("etltab").Where("finr = ? and status != 1", t.Finr).Find(&data)
if err != nil {
@ -234,7 +236,7 @@ func (t *Etltab) SelectAll() ([]Etltab, error) {
}
data[k].Valst = datalist
}
glog.InfoExtln("Etl","len(data) :",len(data))
return data, nil
}


+ 1
- 1
web/models/me_attribute_model.go View File

@ -416,7 +416,7 @@ func (t *MeAttribute) ReadData() (string, string, error) {
name := utils.MakeOrderSn("属性主数据")
filename := name + ".xlsx" //文件名
exceldir := utils.EnsureDir("web/public/uploadxlsx") // 目录+文件名
exceldir,_ := utils.GetCurrentPath("web/public/uploadxlsx") // 目录+文件名
excelfile := exceldir + "/" + filename
err = file.Save(excelfile)
if err != nil {


+ 1
- 1
web/models/me_part_model.go View File

@ -251,7 +251,7 @@ func (t *MePart) ReadData() (string, string, error) {
name := utils.MakeOrderSn("零件主数据")
filename := name + ".xlsx" //文件名
exceldir := utils.EnsureDir("web/public/uploadxlsx") // 目录+文件名
exceldir,_ := utils.GetCurrentPath("web/public/uploadxlsx") // 目录+文件名
excelfile := exceldir + "/" + filename
err = file.Save(excelfile)
if err != nil {


+ 1
- 1
web/models/me_partrule_model.go View File

@ -319,7 +319,7 @@ func (t *MePartRule) ReadData() (string, string, error) {
name := utils.MakeOrderSn("零件规则")
filename := name + ".xlsx" //文件名
exceldir := utils.EnsureDir("web/public/uploadxlsx") // 目录+文件名
exceldir,_ := utils.GetCurrentPath("web/public/uploadxlsx") // 目录+文件名
excelfile := exceldir + "/" + filename
err = file.Save(excelfile)
if err != nil {


+ 4
- 4
web/models/pln_forecast_item_dailydemand_model.go View File

@ -892,7 +892,7 @@ func (t *PlnForecastItemDailydemand) ReadExcelMonthdemand(timedate string,articl
name := utils.MakeOrderSn("月报")
filename := name + ".xlsx" //文件名
exceldir := utils.EnsureDir("web/public/uploadxlsx") // 目录+文件名
exceldir,_ := utils.GetCurrentPath("web/public/uploadxlsx") // 目录+文件名
excelfile := exceldir + "/" + filename
err = file.Save(excelfile)
if err != nil {
@ -1068,7 +1068,7 @@ func (t *PlnForecastItemDailydemand) ReadExcelDaydemand(timedate string,articles
name := utils.MakeOrderSn("日报")
filename := name + ".xlsx" //文件名
exceldir := utils.EnsureDir("web/public/uploadxlsx") // 目录+文件名
exceldir,_ := utils.GetCurrentPath("web/public/uploadxlsx") // 目录+文件名
excelfile := exceldir + "/" + filename
err = file.Save(excelfile)
if err != nil {
@ -1245,7 +1245,7 @@ func (t *PlnForecastItemDailydemand) ReadExcelDemand(articles string) (string, s
name := utils.MakeOrderSn("预测统计")
filename := name + ".xlsx" //文件名
exceldir := utils.EnsureDir("web/public/uploadxlsx") // 目录+文件名
exceldir,_ := utils.GetCurrentPath("web/public/uploadxlsx") // 目录+文件名
excelfile := exceldir + "/" + filename
err = file.Save(excelfile)
if err != nil {
@ -2098,7 +2098,7 @@ func (t *PlnForecastItemDailydemand) ReadYearExcelDemand(timedate string,article
name := utils.MakeOrderSn("预测统计")
filename := name + ".xlsx" //文件名
exceldir := utils.EnsureDir("web/public/uploadxlsx") // 目录+文件名
exceldir,_ := utils.GetCurrentPath("web/public/uploadxlsx") // 目录+文件名
excelfile := exceldir + "/" + filename
err = file.Save(excelfile)
if err != nil {


Loading…
Cancel
Save