@@ -0,0 +1,46 @@ | |||||
# Binaries for programs and plugins | |||||
*.exe | |||||
*.exe~ | |||||
*.dll | |||||
*.so | |||||
*.dylib | |||||
# Test binary, built with `go test -c` | |||||
*.test | |||||
# Output of the go coverage tool, specifically when used with LiteIDE | |||||
*.out | |||||
.idea | |||||
.vscode | |||||
*.log | |||||
.DS_Store | |||||
Thumbs.db | |||||
*.swp | |||||
*.swn | |||||
*.swo | |||||
*.swm | |||||
*.7z | |||||
*.zip | |||||
*.rar | |||||
*.tar | |||||
*.tar.gz | |||||
go.sum | |||||
/etc/cfg.yaml | |||||
images | |||||
test/test.json | |||||
etc/cfg.yml | |||||
t.json | |||||
t1.json | |||||
t2.json | |||||
t3.json | |||||
t.go | |||||
wait-for-it.sh | |||||
test.go | |||||
xorm | |||||
test.csv | |||||
nginx.conf | |||||
.devcontainer | |||||
.devcontainer/Dockerfile | |||||
.devcontainer/sources.list | |||||
/t1.go | |||||
/tmp/* | |||||
.idea/* | |||||
/.idea/modules.xml |
@@ -0,0 +1,35 @@ | |||||
# 多重构建,减少镜像大小 | |||||
# 构建:使用golang:1.15版本 | |||||
FROM registry.cn-shenzhen.aliyuncs.com/fnuoos-prd/golang:1.18.4 as build | |||||
# 容器环境变量添加,会覆盖默认的变量值 | |||||
ENV GO111MODULE=on | |||||
ENV GOPROXY=https://goproxy.cn,direct | |||||
ENV TZ="Asia/Shanghai" | |||||
# 设置工作区 | |||||
WORKDIR /go/release | |||||
# 把全部文件添加到/go/release目录 | |||||
ADD . . | |||||
# 编译:把main.go编译成可执行的二进制文件,命名为zyos | |||||
RUN GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -tags netgo -ldflags="-s -w" -installsuffix cgo -o zyos main.go | |||||
FROM ubuntu:xenial as prod | |||||
LABEL maintainer="wuhanqin" | |||||
ENV TZ="Asia/Shanghai" | |||||
COPY static/html static/html | |||||
# 时区纠正 | |||||
RUN rm -f /etc/localtime \ | |||||
&& ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ | |||||
&& echo "Asia/Shanghai" > /etc/timezone | |||||
# 在build阶段复制可执行的go二进制文件app | |||||
COPY --from=build /go/release/zyos ./zyos | |||||
COPY --from=build /go/release/etc/cfg.yml /var/zyos/cfg.yml | |||||
# 启动服务 | |||||
CMD ["./zyos","-c","/var/zyos/cfg.yml"] | |||||
@@ -0,0 +1,34 @@ | |||||
# 多重构建,减少镜像大小 | |||||
# 构建:使用golang:1.15版本 | |||||
FROM registry.cn-shenzhen.aliyuncs.com/fnuoos-prd/golang:1.18.4 as build | |||||
# 容器环境变量添加,会覆盖默认的变量值 | |||||
ENV GO111MODULE=on | |||||
ENV GOPROXY=https://goproxy.cn,direct | |||||
ENV TZ="Asia/Shanghai" | |||||
# 设置工作区 | |||||
WORKDIR /go/release | |||||
# 把全部文件添加到/go/release目录 | |||||
ADD . . | |||||
# 编译:把main.go编译成可执行的二进制文件,命名为zyos | |||||
RUN GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -tags netgo -ldflags="-s -w" -installsuffix cgo -o zyos_mall_task cmd/task/main.go | |||||
FROM ubuntu:xenial as prod | |||||
LABEL maintainer="wuhanqin" | |||||
ENV TZ="Asia/Shanghai" | |||||
COPY static/html static/html | |||||
# 时区纠正 | |||||
RUN rm -f /etc/localtime \ | |||||
&& ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ | |||||
&& echo "Asia/Shanghai" > /etc/timezone | |||||
# 在build阶段复制可执行的go二进制文件app | |||||
COPY --from=build /go/release/zyos_mall_task ./zyos_mall_task | |||||
COPY --from=build /go/release/etc/task.yml /var/zyos/task.yml | |||||
# 启动服务 | |||||
CMD ["./zyos_mall_task","-c","/var/zyos/task.yml"] | |||||
@@ -0,0 +1,32 @@ | |||||
.PHONY: build clean tool lint help | |||||
APP=applet | |||||
all: build | |||||
build: | |||||
go build -o ./bin/$(APP) ./cmd/main.go | |||||
lite: | |||||
go build -ldflags "-s -w" -o ./bin/$(APP) ./cmd/main.go | |||||
install: | |||||
#@go build -v . | |||||
go install ./cmd/... | |||||
tool: | |||||
go vet ./...; true | |||||
gofmt -w . | |||||
lint: | |||||
golint ./... | |||||
clean: | |||||
rm -rf go-gin-example | |||||
go clean -i . | |||||
help: | |||||
@echo "make: compile packages and dependencies" | |||||
@echo "make tool: run specified go tool" | |||||
@echo "make lint: golint ./..." | |||||
@echo "make clean: remove object files and cached files" |
@@ -0,0 +1,48 @@ | |||||
# 广告站长平台 | |||||
## 要看 nginx.conf 和 wap conf | |||||
## 层级介绍 | |||||
- hdl 做接收数据的报错, 数据校验 | |||||
- svc 做数据处理的报错, 数据转换 | |||||
- lib 只抛出错误给hdl或者svc进行处理, 不做数据校验 | |||||
- db 可以处理db错误,其它错误返回给svc进行处理 | |||||
- mw 中间件 | |||||
- md 结构体 | |||||
#### 介绍 | |||||
基于gin的接口小程序 | |||||
#### 软件架构 | |||||
软件架构说明 | |||||
#### 安装教程 | |||||
1. xxxx | |||||
2. xxxx | |||||
3. xxxx | |||||
#### 使用说明 | |||||
1. xxxx | |||||
2. xxxx | |||||
3. xxxx | |||||
#### 参与贡献 | |||||
1. Fork 本仓库 | |||||
2. 新建 Feat_xxx 分支 | |||||
3. 提交代码 | |||||
4. 新建 Pull Request | |||||
## swagger | |||||
``` | |||||
// 参考:https://segmentfault.com/a/1190000013808421 | |||||
// 安装命令行 | |||||
go get -u github.com/swaggo/swag/cmd/swag | |||||
// 生成 | |||||
swag init | |||||
``` |
@@ -0,0 +1,44 @@ | |||||
package cfg | |||||
import ( | |||||
"time" | |||||
) | |||||
type Config struct { | |||||
Debug bool `yaml:"debug"` | |||||
Prd bool `yaml:"prd"` | |||||
CurlDebug bool `yaml:"curldebug"` | |||||
SrvAddr string `yaml:"srv_addr"` | |||||
RedisAddr string `yaml:"redis_addr"` | |||||
DB DBCfg `yaml:"db"` | |||||
Log LogCfg `yaml:"log"` | |||||
Local bool | |||||
} | |||||
// 数据库配置结构体 | |||||
type DBCfg struct { | |||||
Host string `yaml:"host"` //ip及端口 | |||||
Name string `yaml:"name"` //库名 | |||||
User string `yaml:"user"` //用户 | |||||
Psw string `yaml:"psw"` //密码 | |||||
ShowLog bool `yaml:"show_log"` //是否显示SQL语句 | |||||
MaxLifetime time.Duration `yaml:"max_lifetime"` | |||||
MaxOpenConns int `yaml:"max_open_conns"` | |||||
MaxIdleConns int `yaml:"max_idle_conns"` | |||||
Path string `yaml:"path"` //日志文件存放路径 | |||||
} | |||||
// 日志配置结构体 | |||||
type LogCfg struct { | |||||
AppName string `yaml:"app_name" ` | |||||
Level string `yaml:"level"` | |||||
IsStdOut bool `yaml:"is_stdout"` | |||||
TimeFormat string `yaml:"time_format"` // second, milli, nano, standard, iso, | |||||
Encoding string `yaml:"encoding"` // console, json | |||||
IsFileOut bool `yaml:"is_file_out"` | |||||
FileDir string `yaml:"file_dir"` | |||||
FileName string `yaml:"file_name"` | |||||
FileMaxSize int `yaml:"file_max_size"` | |||||
FileMaxAge int `yaml:"file_max_age"` | |||||
} |
@@ -0,0 +1,3 @@ | |||||
package cfg | |||||
// 统一管理缓存 |
@@ -0,0 +1,9 @@ | |||||
package cfg | |||||
import ( | |||||
"applet/app/utils/cache" | |||||
) | |||||
func InitCache() { | |||||
cache.NewRedis(RedisAddr) | |||||
} |
@@ -0,0 +1,50 @@ | |||||
package cfg | |||||
import ( | |||||
"flag" | |||||
"io/ioutil" | |||||
"gopkg.in/yaml.v2" | |||||
) | |||||
// 配置文件数据,全局变量 | |||||
var ( | |||||
Debug bool | |||||
Prd bool | |||||
CurlDebug bool | |||||
SrvAddr string | |||||
RedisAddr string | |||||
DB *DBCfg | |||||
Log *LogCfg | |||||
Local bool | |||||
) | |||||
// 初始化配置文件,将cfg.yml读入到内存 | |||||
func InitCfg() { | |||||
//用指定的名称、默认值、使用信息注册一个string类型flag。 | |||||
path := flag.String("c", "etc/cfg.yml", "config file") | |||||
//解析命令行参数写入注册的flag里。 | |||||
//解析之后,flag的值可以直接使用。 | |||||
flag.Parse() | |||||
var ( | |||||
c []byte | |||||
err error | |||||
conf *Config | |||||
) | |||||
if c, err = ioutil.ReadFile(*path); err != nil { | |||||
panic(err) | |||||
} | |||||
//yaml.Unmarshal反序列化映射到Config | |||||
if err = yaml.Unmarshal(c, &conf); err != nil { | |||||
panic(err) | |||||
} | |||||
//数据读入内存 | |||||
Prd = conf.Prd | |||||
Debug = conf.Debug | |||||
Local = conf.Local | |||||
CurlDebug = conf.CurlDebug | |||||
DB = &conf.DB | |||||
Log = &conf.Log | |||||
RedisAddr = conf.RedisAddr | |||||
SrvAddr = conf.SrvAddr | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package cfg | |||||
import "applet/app/utils/logx" | |||||
func InitLog() { | |||||
logx.InitDefaultLogger(&logx.LogConfig{ | |||||
AppName: Log.AppName, | |||||
Level: Log.Level, | |||||
StacktraceLevel: "error", | |||||
IsStdOut: Log.IsStdOut, | |||||
TimeFormat: Log.TimeFormat, | |||||
Encoding: Log.Encoding, | |||||
IsFileOut: Log.IsFileOut, | |||||
FileDir: Log.FileDir, | |||||
FileName: Log.FileName, | |||||
FileMaxSize: Log.FileMaxSize, | |||||
FileMaxAge: Log.FileMaxAge, | |||||
Skip: 2, | |||||
}) | |||||
} |
@@ -0,0 +1,43 @@ | |||||
package cfg | |||||
import ( | |||||
"flag" | |||||
"io/ioutil" | |||||
"gopkg.in/yaml.v2" | |||||
mc "applet/app/utils/cache/cache" | |||||
"applet/app/utils/logx" | |||||
) | |||||
func InitTaskCfg() { | |||||
path := flag.String("c", "etc/task.yml", "config file") | |||||
flag.Parse() | |||||
var ( | |||||
c []byte | |||||
err error | |||||
conf *Config | |||||
) | |||||
if c, err = ioutil.ReadFile(*path); err != nil { | |||||
panic(err) | |||||
} | |||||
if err = yaml.Unmarshal(c, &conf); err != nil { | |||||
panic(err) | |||||
} | |||||
Prd = conf.Prd | |||||
Debug = conf.Debug | |||||
DB = &conf.DB | |||||
Log = &conf.Log | |||||
RedisAddr = conf.RedisAddr | |||||
Local = conf.Local | |||||
} | |||||
var MemCache mc.Cache | |||||
func InitMemCache() { | |||||
var err error | |||||
MemCache, err = mc.NewCache("memory", `{"interval":60}`) | |||||
if err != nil { | |||||
logx.Fatal(err.Error()) | |||||
} | |||||
} |
@@ -0,0 +1,44 @@ | |||||
package dao | |||||
import ( | |||||
"applet/app/db/model" | |||||
"xorm.io/xorm" | |||||
) | |||||
type AdminDao interface { | |||||
CreateAdminId() (admId int, err error) | |||||
AdminDeleteBySession(session *xorm.Session, admId interface{}) (int64, error) | |||||
GetAdmin(id int) (m *model.Admin, err error) | |||||
GetAdminByUserName(userName string) (m *model.Admin, err error) | |||||
GetAdminRolePermission(admId int) (list []*AdminRolePermission, total int64, err error) | |||||
FindAdmin(username string, state, page, limit int) (list []model.Admin, total int64, err error) | |||||
UpdateAdmin(m *model.Admin, columns ...string) (int64, error) | |||||
AdminInsert(m *model.Admin) (int64, error) | |||||
FindAdminRolePermissionGroup(admId int) (list []*AdminRolePermissionGroup, total int64, err error) | |||||
} | |||||
type AdminRolePermissionGroup struct { | |||||
model.Admin `xorm:"extends"` | |||||
model.AdminRole `xorm:"extends"` | |||||
model.Role `xorm:"extends"` | |||||
model.RolePermissionGroup `xorm:"extends"` | |||||
model.PermissionGroup `xorm:"extends"` | |||||
} | |||||
func (AdminRolePermissionGroup) TableName() string { | |||||
return "admin" | |||||
} | |||||
type AdminRolePermission struct { | |||||
model.Admin `xorm:"extends"` | |||||
model.AdminRole `xorm:"extends"` | |||||
model.Role `xorm:"extends"` | |||||
model.RolePermissionGroup `xorm:"extends"` | |||||
model.PermissionGroup `xorm:"extends"` | |||||
model.PermissionGroupPermission `xorm:"extends"` | |||||
model.Permission `xorm:"extends"` | |||||
} | |||||
func (AdminRolePermission) TableName() string { | |||||
return "admin" | |||||
} |
@@ -0,0 +1,25 @@ | |||||
package dao | |||||
import ( | |||||
"applet/app/db/model" | |||||
"xorm.io/xorm" | |||||
) | |||||
type AdminRoleDao interface { | |||||
FindAdminRole(id int) (*[]model.AdminRole, error) | |||||
FindAdminRoleByRoleId(id int) (*[]model.AdminRole, error) | |||||
AdminDeleteBySessionForAdmId(session *xorm.Session, admId interface{}) (int64, error) | |||||
GetAdminRoleByRole(id int) (m *model.AdminRole, err error) | |||||
AdminRoleDeleteForRoleBySession(session *xorm.Session, roleId interface{}) (int64, error) | |||||
AdminRoleDeleteBySession(session *xorm.Session, id interface{}) (int64, error) | |||||
BatchAddAdminRoleBySession(session *xorm.Session, mm []*model.AdminRole) (int64, error) | |||||
} | |||||
type AdminRole struct { | |||||
model.Admin `xorm:"extends"` | |||||
model.AdminRole `xorm:"extends"` | |||||
} | |||||
func (AdminRole) TableName() string { | |||||
return "admin_role" | |||||
} |
@@ -0,0 +1,5 @@ | |||||
package dao | |||||
type PermissionDao interface { | |||||
//TODO:: You can add specific method definitions here | |||||
} |
@@ -0,0 +1,8 @@ | |||||
package dao | |||||
import "applet/app/db/model" | |||||
type PermissionGroupDao interface { | |||||
FindPermissionGroup() (*[]model.PermissionGroup, error) | |||||
FindPermissionGroupV2() (*[]model.PermissionGroup, error) | |||||
} |
@@ -0,0 +1,5 @@ | |||||
package dao | |||||
type PermissionGroupPermissionDao interface { | |||||
//TODO:: You can add specific method definitions here | |||||
} |
@@ -0,0 +1,25 @@ | |||||
package dao | |||||
import ( | |||||
"applet/app/db/model" | |||||
"xorm.io/xorm" | |||||
) | |||||
type RoleDao interface { | |||||
FindRole() (*[]model.Role, error) | |||||
GetRole() (m *model.Role, err error) | |||||
UpdateRole(m *model.Role, columns ...string) (int64, error) | |||||
RoleDeleteBySession(session *xorm.Session, id interface{}) (int64, error) | |||||
RoleInsert(m *model.Role) (int, error) | |||||
FindPermissionGroupByRole(roleId int) (list []*RolePermissionGroup, total int64, err error) | |||||
} | |||||
type RolePermissionGroup struct { | |||||
model.Role `xorm:"extends"` | |||||
model.RolePermissionGroup `xorm:"extends"` | |||||
model.PermissionGroup `xorm:"extends"` | |||||
} | |||||
func (RolePermissionGroup) TableName() string { | |||||
return "role" | |||||
} |
@@ -0,0 +1,11 @@ | |||||
package dao | |||||
import ( | |||||
"applet/app/db/model" | |||||
"xorm.io/xorm" | |||||
) | |||||
type RolePermissionGroupDao interface { | |||||
RolePermissionGroupDeleteForRoleBySession(session *xorm.Session, roleId interface{}) (int64, error) | |||||
BatchAddRolePermissionGroupBySession(session *xorm.Session, mm []*model.RolePermissionGroup) (int64, error) | |||||
} |
@@ -0,0 +1,112 @@ | |||||
package db | |||||
import ( | |||||
"database/sql" | |||||
"fmt" | |||||
"os" | |||||
"time" | |||||
_ "github.com/go-sql-driver/mysql" //必须导入mysql驱动,否则会panic | |||||
"xorm.io/xorm" | |||||
"xorm.io/xorm/log" | |||||
"applet/app/cfg" | |||||
"applet/app/utils/logx" | |||||
) | |||||
var Db *xorm.Engine | |||||
//根据DB配置文件初始化数据库 | |||||
func InitDB(c *cfg.DBCfg) error { | |||||
var ( | |||||
err error | |||||
f *os.File | |||||
) | |||||
//创建Orm引擎 | |||||
if Db, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4", c.User, c.Psw, c.Host, c.Name)); err != nil { | |||||
return err | |||||
} | |||||
Db.SetConnMaxLifetime(c.MaxLifetime * time.Second) //设置最长连接时间 | |||||
Db.SetMaxOpenConns(c.MaxOpenConns) //设置最大打开连接数 | |||||
Db.SetMaxIdleConns(c.MaxIdleConns) //设置连接池的空闲数大小 | |||||
if err = Db.Ping(); err != nil { //尝试ping数据库 | |||||
return err | |||||
} | |||||
if c.ShowLog { //根据配置文件设置日志 | |||||
Db.ShowSQL(true) //设置是否打印sql | |||||
Db.Logger().SetLevel(0) //设置日志等级 | |||||
//修改日志文件存放路径文件名是%s.log | |||||
path := fmt.Sprintf(c.Path, c.Name) | |||||
f, err = os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777) | |||||
if err != nil { | |||||
os.RemoveAll(c.Path) | |||||
if f, err = os.OpenFile(c.Path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777); err != nil { | |||||
return err | |||||
} | |||||
} | |||||
logger := log.NewSimpleLogger(f) | |||||
logger.ShowSQL(true) | |||||
Db.SetLogger(logger) | |||||
} | |||||
return nil | |||||
} | |||||
/********************************************* 公用方法 *********************************************/ | |||||
// 数据批量插入 | |||||
func DbInsertBatch(Db *xorm.Engine, m ...interface{}) error { | |||||
if len(m) == 0 { | |||||
return nil | |||||
} | |||||
id, err := Db.Insert(m...) | |||||
if id == 0 || err != nil { | |||||
return logx.Warn("cannot insert data :", err) | |||||
} | |||||
return nil | |||||
} | |||||
// QueryNativeString 查询原生sql | |||||
func QueryNativeString(Db *xorm.Engine, sql string, args ...interface{}) ([]map[string]string, error) { | |||||
results, err := Db.SQL(sql, args...).QueryString() | |||||
return results, err | |||||
} | |||||
// UpdateComm common update | |||||
func UpdateComm(Db *xorm.Engine, id interface{}, model interface{}) (int64, error) { | |||||
row, err := Db.ID(id).Update(model) | |||||
return row, err | |||||
} | |||||
// InsertComm common insert | |||||
func InsertComm(Db *xorm.Engine, model interface{}) (int64, error) { | |||||
row, err := Db.InsertOne(model) | |||||
return row, err | |||||
} | |||||
// ExecuteOriginalSql 执行原生sql | |||||
func ExecuteOriginalSql(session *xorm.Session, sql string) (sql.Result, error) { | |||||
result, err := session.Exec(sql) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
return result, nil | |||||
} | |||||
// GetComm | |||||
// payload *model | |||||
// return *model,has,err | |||||
func GetComm(Db *xorm.Engine, model interface{}) (interface{}, bool, error) { | |||||
has, err := Db.Get(model) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return nil, false, err | |||||
} | |||||
return model, has, nil | |||||
} | |||||
// InsertCommWithSession common insert | |||||
func InsertCommWithSession(session *xorm.Session, model interface{}) (int64, error) { | |||||
row, err := session.InsertOne(model) | |||||
return row, err | |||||
} |
@@ -0,0 +1,82 @@ | |||||
package db | |||||
import ( | |||||
"applet/app/db/model" | |||||
"applet/app/md" | |||||
"applet/app/utils/cache" | |||||
"applet/app/utils/logx" | |||||
"fmt" | |||||
"xorm.io/xorm" | |||||
) | |||||
// 系统配置get | |||||
func SysCfgGetAll(Db *xorm.Engine) (*[]model.SysCfg, error) { | |||||
var cfgList []model.SysCfg | |||||
if err := Db.Cols("key,val,memo").Find(&cfgList); err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &cfgList, nil | |||||
} | |||||
// 获取一条记录 | |||||
func SysCfgGetOne(Db *xorm.Engine, key string) (*model.SysCfg, error) { | |||||
var cfgList model.SysCfg | |||||
if has, err := Db.Where("`key`=?", key).Get(&cfgList); err != nil || has == false { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &cfgList, nil | |||||
} | |||||
// 返回最后插入id | |||||
func SysCfgInsert(Db *xorm.Engine, key, val, memo string) bool { | |||||
cfg := model.SysCfg{Key: key, Val: val, Memo: memo} | |||||
_, err := Db.InsertOne(&cfg) | |||||
if err != nil { | |||||
logx.Error(err) | |||||
return false | |||||
} | |||||
return true | |||||
} | |||||
func SysCfgUpdate(Db *xorm.Engine, key, val, memo string) bool { | |||||
cfg := model.SysCfg{Key: key, Val: val, Memo: memo} | |||||
_, err := Db.Where("`key`=?", key).Cols("val,memo").Update(&cfg) | |||||
if err != nil { | |||||
logx.Error(err) | |||||
return false | |||||
} | |||||
return true | |||||
} | |||||
func SysCfgGetWithDb(eg *xorm.Engine, masterId string, HKey string) string { | |||||
cacheKey := fmt.Sprintf(md.AppCfgCacheKey, masterId) + HKey | |||||
get, err := cache.GetString(cacheKey) | |||||
if err != nil || get == "" { | |||||
cfg, err := SysCfgGetOne(eg, HKey) | |||||
if err != nil || cfg == nil { | |||||
_ = logx.Error(err) | |||||
return "" | |||||
} | |||||
// key是否存在 | |||||
cacheKeyExist := false | |||||
if cache.Exists(cacheKey) { | |||||
cacheKeyExist = true | |||||
} | |||||
// 设置缓存 | |||||
_, err = cache.SetEx(cacheKey, cfg.Val, 30) | |||||
if err != nil { | |||||
_ = logx.Error(err) | |||||
return "" | |||||
} | |||||
if !cacheKeyExist { // 如果是首次设置 设置过期时间 | |||||
_, err := cache.Expire(cacheKey, md.CfgCacheTime) | |||||
if err != nil { | |||||
_ = logx.Error(err) | |||||
return "" | |||||
} | |||||
} | |||||
return cfg.Val | |||||
} | |||||
return get | |||||
} |
@@ -0,0 +1,104 @@ | |||||
package db | |||||
import ( | |||||
"fmt" | |||||
"os" | |||||
"time" | |||||
"xorm.io/xorm" | |||||
"xorm.io/xorm/log" | |||||
"applet/app/cfg" | |||||
"applet/app/db/model" | |||||
"applet/app/utils/logx" | |||||
) | |||||
var DBs map[string]*xorm.Engine | |||||
// 每个站长都要有自己的syscfg 缓存, 键是站长id,值是缓存名 | |||||
// var SysCfgMapKey map[string]string | |||||
func NewDB(c *cfg.DBCfg) (*xorm.Engine, error) { | |||||
db, err := xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4", c.User, c.Psw, c.Host, c.Name)) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
db.SetConnMaxLifetime(c.MaxLifetime * time.Second) | |||||
db.SetMaxOpenConns(c.MaxOpenConns) | |||||
db.SetMaxIdleConns(c.MaxIdleConns) | |||||
err = db.Ping() | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if c.ShowLog { | |||||
db.ShowSQL(true) | |||||
db.Logger().SetLevel(log.LOG_DEBUG) | |||||
f, err := os.OpenFile(c.Path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777) | |||||
if err != nil { | |||||
os.RemoveAll(c.Path) | |||||
if f, err = os.OpenFile(c.Path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777); err != nil { | |||||
return nil, err | |||||
} | |||||
} | |||||
logger := log.NewSimpleLogger(f) | |||||
logger.ShowSQL(true) | |||||
db.SetLogger(logger) | |||||
} | |||||
return db, nil | |||||
} | |||||
// InitDBs is 初始化多数据库 | |||||
func InitDBs(ch chan int) { | |||||
// 初始化多数据库 | |||||
var tables *[]model.DbMapping | |||||
InitMapDbs(cfg.DB, cfg.Prd) | |||||
ch <- 1 | |||||
// 每10s 查询一次模板数据库的db mapping 表,如果有新增数据库记录,则添加到 DBs中 | |||||
ticker := time.NewTicker(time.Duration(time.Second * 120)) | |||||
for range ticker.C { | |||||
if cfg.Prd { | |||||
tables = GetAllDatabasePrd() //默认获取全部 | |||||
} else { | |||||
tables = GetAllDatabaseDev() //默认获取全部 | |||||
} | |||||
if tables == nil { | |||||
logx.Warn("no database tables data") | |||||
continue | |||||
} | |||||
for _, item := range *tables { | |||||
_, ok := DBs[item.DbMasterId] | |||||
if !ok { | |||||
// 不在db.DBs 则添加进去 | |||||
dbCfg := cfg.DBCfg{ | |||||
Name: item.DbName, | |||||
ShowLog: cfg.DB.ShowLog, | |||||
MaxLifetime: cfg.DB.MaxLifetime, | |||||
MaxOpenConns: cfg.DB.MaxOpenConns, | |||||
MaxIdleConns: cfg.DB.MaxIdleConns, | |||||
Path: fmt.Sprintf(cfg.DB.Path, item.DbName), | |||||
} | |||||
if item.DbHost != "" && item.DbUsername != "" && item.DbPassword != "" { | |||||
dbCfg.Host = item.DbHost | |||||
dbCfg.User = item.DbUsername | |||||
dbCfg.Psw = item.DbPassword | |||||
} else { | |||||
dbCfg.Host = cfg.DB.Host | |||||
dbCfg.User = cfg.DB.User | |||||
dbCfg.Psw = cfg.DB.Psw | |||||
} | |||||
e, err := NewDB(&dbCfg) | |||||
if err != nil || e == nil { | |||||
logx.Warnf("db engine can't create, please check config, params[host:%s, user:%s, psw: %s, name: %s], err: %v", dbCfg.Host, dbCfg.User, dbCfg.Psw, dbCfg.Name, err) | |||||
} else { | |||||
DBs[item.DbMasterId] = e | |||||
} | |||||
} | |||||
// 如果 被禁用则删除 | |||||
if item.DeletedAt == 1 { | |||||
logx.Infof("%s have been removed", item.DbMasterId) | |||||
delete(DBs, item.DbMasterId) | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,194 @@ | |||||
package db | |||||
import ( | |||||
"errors" | |||||
"fmt" | |||||
"xorm.io/xorm" | |||||
"applet/app/cfg" | |||||
"applet/app/db/model" | |||||
"applet/app/utils/logx" | |||||
) | |||||
func MapBaseExists() (bool, error) { | |||||
return Db.IsTableExist("db_mapping") | |||||
} | |||||
func InitMapDbs(c *cfg.DBCfg, prd bool) { | |||||
var tables *[]model.DbMapping | |||||
exists, err := MapBaseExists() | |||||
if !exists || err != nil { | |||||
logx.Fatalf("db_mapping not exists : %v", err) | |||||
} | |||||
// tables := MapAllDatabases(debug) | |||||
if prd { | |||||
tables = GetAllDatabasePrd() //debug 获取生产 | |||||
} else { | |||||
tables = GetAllDatabaseDev() //debug 获取开发 | |||||
} | |||||
if tables == nil { | |||||
logx.Fatal("no database tables data") | |||||
} | |||||
var e *xorm.Engine | |||||
DBs = map[string]*xorm.Engine{} | |||||
for _, v := range *tables { | |||||
if v.DbName != "" && v.DeletedAt == 0 && v.DbName != c.Name { | |||||
dbCfg := cfg.DBCfg{ | |||||
Name: v.DbName, | |||||
ShowLog: c.ShowLog, | |||||
MaxLifetime: c.MaxLifetime, | |||||
MaxOpenConns: c.MaxOpenConns, | |||||
MaxIdleConns: c.MaxIdleConns, | |||||
Path: fmt.Sprintf(c.Path, v.DbName), | |||||
} | |||||
if v.DbHost != "" && v.DbUsername != "" && v.DbPassword != "" { | |||||
dbCfg.Host = v.DbHost | |||||
dbCfg.User = v.DbUsername | |||||
dbCfg.Psw = v.DbPassword | |||||
} else { | |||||
dbCfg.Host = c.Host | |||||
dbCfg.User = c.User | |||||
dbCfg.Psw = c.Psw | |||||
} | |||||
e, err = NewDB(&dbCfg) | |||||
if err != nil || e == nil { | |||||
logx.Warnf("db engine can't create, please check config, params[host:%s, user:%s, psw: %s, name: %s], err: %v", dbCfg.Host, dbCfg.User, dbCfg.Psw, dbCfg.Name, err) | |||||
} else { | |||||
DBs[v.DbMasterId] = e | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func MapAllDatabases(debug bool) *[]model.DbMapping { | |||||
sql := "`db_name` != ?" | |||||
if debug { | |||||
sql = "`db_name` = ?" | |||||
} | |||||
var m []model.DbMapping | |||||
if err := Db.Where(sql, cfg.DB.Name).Find(&m); err != nil || len(m) == 0 { | |||||
logx.Warn(err) | |||||
return nil | |||||
} | |||||
return &m | |||||
} | |||||
// GetAllDatabasePrd is 获取生成库 所有db 除了 deleted_at = 1 的 | |||||
func GetAllDatabasePrd() *[]model.DbMapping { | |||||
var m []model.DbMapping | |||||
if err := Db.Where("deleted_at != ? AND is_dev = '0' ", 1).Find(&m); err != nil || len(m) == 0 { | |||||
logx.Warn(err) | |||||
return nil | |||||
} | |||||
return &m | |||||
} | |||||
// GetAllDatabaseDev is 获取开发库 所有db 除了 deleted_at = 1 的 | |||||
func GetAllDatabaseDev() *[]model.DbMapping { | |||||
var m []model.DbMapping | |||||
var err error | |||||
fmt.Println("cfg.Local is: ", cfg.Local) | |||||
if cfg.Local { // 本地调试 加快速度 | |||||
fmt.Println("notice:LOCAL TEST, only masterId:** 123456 ** available!") | |||||
err = Db.Where("deleted_at != ? AND is_dev = '1' AND db_master_id=?", 1, 123456).Find(&m) | |||||
} else { | |||||
err = Db.Where("deleted_at != ? AND is_dev = '1' ", 1).Find(&m) | |||||
} | |||||
//err := Db.Where("deleted_at != ? AND is_dev = '1' and db_master_id='123456'", 1).Find(&m) | |||||
if err != nil || len(m) == 0 { | |||||
logx.Warn(err) | |||||
return nil | |||||
} | |||||
return &m | |||||
} | |||||
//GetDatabaseByMasterID is 根据站长id 获取对应的的数据库信息 | |||||
func GetDatabaseByMasterID(Db *xorm.Engine, id string) (*model.DbMapping, error) { | |||||
var m model.DbMapping | |||||
has, err := Db.Where("db_master_id=?", id).Get(&m) | |||||
if !has { | |||||
return nil, errors.New("Not Found DB data by " + id) | |||||
} | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if m.DbHost == "" { | |||||
m.DbHost = cfg.DB.Host | |||||
m.DbUsername = cfg.DB.User | |||||
m.DbPassword = cfg.DB.Psw | |||||
} | |||||
return &m, nil | |||||
} | |||||
//SessionGetDatabaseByMasterID is 根据站长id 获取对应的的数据库信息 | |||||
func SessionGetDatabaseByMasterID(Db *xorm.Session, id string) (*model.DbMapping, error) { | |||||
var m model.DbMapping | |||||
has, err := Db.Where("db_master_id=?", id).Get(&m) | |||||
if !has { | |||||
return nil, errors.New("Not Found DB data by " + id) | |||||
} | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if m.DbHost == "" { | |||||
m.DbHost = cfg.DB.Host | |||||
m.DbName = cfg.DB.Name | |||||
m.DbUsername = cfg.DB.User | |||||
m.DbPassword = cfg.DB.Psw | |||||
} | |||||
return &m, nil | |||||
} | |||||
// 获取自动任务队列 | |||||
func MapCrontabCfg(eg *xorm.Engine) *[]model.SysCfg { | |||||
var c []model.SysCfg | |||||
// 数据库查询如果有下划线会认为是一个任意字符 | |||||
if err := eg.Where("`key` LIKE 'mall_cron\\_%' AND val != ''").Cols("`key`,`val`").Find(&c); err != nil || len(c) == 0 { | |||||
logx.Warn(err) | |||||
return nil | |||||
} | |||||
return &c | |||||
} | |||||
// 获取官方域名 | |||||
func GetOfficialDomainInfoByType(Db *xorm.Engine, masterId, key string) (string, error) { | |||||
type SysCfg struct { | |||||
K string | |||||
V string | |||||
Memo string | |||||
} | |||||
var domainBase SysCfg | |||||
has, err := Db.Where("k=?", "domain_base").Get(&domainBase) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
if has == false { | |||||
return "", errors.New("can not find key by : domain_base") | |||||
} | |||||
if key == "wap" { | |||||
return "h5." + masterId + "." + domainBase.V, nil | |||||
} | |||||
if key == "api" { | |||||
var apiDomain SysCfg | |||||
has, err = Db.Where("k=?", "domain_api_base").Get(&apiDomain) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
if has == false { | |||||
return "", errors.New("can not find key by : domain_api_base") | |||||
} | |||||
return apiDomain.V, nil | |||||
} | |||||
if key == "admin" { | |||||
return "admin." + masterId + "." + domainBase.V, nil | |||||
} | |||||
// 默认返回H5的 | |||||
return "h5." + masterId + "." + domainBase.V, nil | |||||
} |
@@ -0,0 +1,115 @@ | |||||
package implement | |||||
import ( | |||||
"applet/app/db/dao" | |||||
"applet/app/db/model" | |||||
"applet/app/utils/logx" | |||||
"reflect" | |||||
"xorm.io/xorm" | |||||
) | |||||
func NewAdminDb(engine *xorm.Engine) dao.AdminDao { | |||||
return &AdminDb{Db: engine} | |||||
} | |||||
type AdminDb struct { | |||||
Db *xorm.Engine | |||||
} | |||||
func (a AdminDb) CreateAdminId() (admId int, err error) { | |||||
m := new(model.Admin) | |||||
has, err := a.Db.Desc("adm_id").Get(m) | |||||
if err != nil { | |||||
return 0, logx.Error(err) | |||||
} | |||||
if has == false { | |||||
return 0, nil | |||||
} | |||||
admId = m.AdmId + 1 | |||||
return admId, nil | |||||
} | |||||
func (a AdminDb) AdminDeleteBySession(session *xorm.Session, admId interface{}) (int64, error) { | |||||
if reflect.TypeOf(admId).Kind() == reflect.Slice { | |||||
return session.In("adm_id", admId).Delete(model.Admin{}) | |||||
} else { | |||||
return session.Where("id = ?", admId).Delete(model.Admin{}) | |||||
} | |||||
} | |||||
func (a AdminDb) GetAdmin(id int) (m *model.Admin, err error) { | |||||
m = new(model.Admin) | |||||
has, err := a.Db.Where("adm_id =?", id).Get(m) | |||||
if err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
if has == false { | |||||
return nil, nil | |||||
} | |||||
return m, nil | |||||
} | |||||
func (a AdminDb) GetAdminByUserName(userName string) (m *model.Admin, err error) { | |||||
m = new(model.Admin) | |||||
has, err := a.Db.Where("username =?", userName).Get(m) | |||||
if err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
if has == false { | |||||
return nil, nil | |||||
} | |||||
return m, nil | |||||
} | |||||
func (a AdminDb) GetAdminRolePermission(admId int) (list []*dao.AdminRolePermission, total int64, err error) { | |||||
total, err = a.Db.Where("admin.adm_id =?", admId). | |||||
Join("LEFT", "admin_role", "admin.adm_id = admin_role.adm_id"). | |||||
Join("LEFT", "role", "admin_role.role_id = role.id"). | |||||
Join("LEFT", "role_permission_group", "role.id = role_permission_group.role_id"). | |||||
Join("LEFT", "permission_group", "role_permission_group.group_id = permission_group.id"). | |||||
Join("LEFT", "permission_group_permission", "permission_group.id = permission_group_permission.group_id"). | |||||
Join("LEFT", "permission", "permission_group_permission.permission_id = permission.id"). | |||||
FindAndCount(&list) | |||||
return | |||||
} | |||||
func (a AdminDb) FindAdmin(username string, state, page, limit int) (list []model.Admin, total int64, err error) { | |||||
sess := a.Db.Desc("adm_id").Limit(limit, (page-1)*limit) | |||||
if username != "" { | |||||
sess.And("username like ?", "%"+username+"%") | |||||
} | |||||
if state != 0 { | |||||
sess.And("state = ?", state) | |||||
} | |||||
total, err = sess.FindAndCount(&list) | |||||
if err != nil { | |||||
return nil, 0, err | |||||
} | |||||
return | |||||
} | |||||
func (a AdminDb) UpdateAdmin(m *model.Admin, columns ...string) (int64, error) { | |||||
affected, err := a.Db.Where("adm_id =?", m.AdmId).Cols(columns...).Update(m) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
return affected, nil | |||||
} | |||||
func (a AdminDb) AdminInsert(m *model.Admin) (int64, error) { | |||||
insertAffected, err := a.Db.InsertOne(m) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
return insertAffected, nil | |||||
} | |||||
func (a AdminDb) FindAdminRolePermissionGroup(admId int) (list []*dao.AdminRolePermissionGroup, total int64, err error) { | |||||
total, err = a.Db.Where("admin.adm_id =?", admId). | |||||
Join("LEFT", "admin_role", "admin.adm_id = admin_role.adm_id"). | |||||
Join("LEFT", "role", "admin_role.role_id = role.id"). | |||||
//Join("LEFT", "role_permission_group", "role.id = role_permission_group.role_id"). | |||||
//Join("LEFT", "permission_group", "role_permission_group.group_id = permission_group.id"). | |||||
FindAndCount(&list) | |||||
return | |||||
} |
@@ -0,0 +1,77 @@ | |||||
package implement | |||||
import ( | |||||
"applet/app/db/dao" | |||||
"applet/app/db/model" | |||||
"applet/app/utils/logx" | |||||
"reflect" | |||||
"xorm.io/xorm" | |||||
) | |||||
func NewAdminRoleDb(engine *xorm.Engine) dao.AdminRoleDao { | |||||
return &AdminRoleDb{Db: engine} | |||||
} | |||||
type AdminRoleDb struct { | |||||
Db *xorm.Engine | |||||
} | |||||
func (a AdminRoleDb) FindAdminRole(id int) (*[]model.AdminRole, error) { | |||||
var m []model.AdminRole | |||||
if err := a.Db.Where("adm_id =?", id).Find(&m); err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &m, nil | |||||
} | |||||
func (a AdminRoleDb) FindAdminRoleByRoleId(id int) (*[]model.AdminRole, error) { | |||||
var m []model.AdminRole | |||||
if err := a.Db.Where("role_id =?", id).Find(&m); err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &m, nil | |||||
} | |||||
func (a AdminRoleDb) AdminDeleteBySessionForAdmId(session *xorm.Session, admId interface{}) (int64, error) { | |||||
if reflect.TypeOf(admId).Kind() == reflect.Slice { | |||||
return session.In("adm_id", admId).Delete(model.AdminRole{}) | |||||
} else { | |||||
return session.Where("adm_id = ?", admId).Delete(model.AdminRole{}) | |||||
} | |||||
} | |||||
func (a AdminRoleDb) GetAdminRoleByRole(id int) (m *model.AdminRole, err error) { | |||||
m = new(model.AdminRole) | |||||
has, err := a.Db.Where("role_id =?", id).Get(m) | |||||
if err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
if has == false { | |||||
return nil, nil | |||||
} | |||||
return m, nil | |||||
} | |||||
func (a AdminRoleDb) AdminRoleDeleteForRoleBySession(session *xorm.Session, roleId interface{}) (int64, error) { | |||||
if reflect.TypeOf(roleId).Kind() == reflect.Slice { | |||||
return session.In("role_id", roleId).Delete(model.AdminRole{}) | |||||
} else { | |||||
return session.Where("role_id = ?", roleId).Delete(model.AdminRole{}) | |||||
} | |||||
} | |||||
func (a AdminRoleDb) AdminRoleDeleteBySession(session *xorm.Session, id interface{}) (int64, error) { | |||||
if reflect.TypeOf(id).Kind() == reflect.Slice { | |||||
return session.In("id", id).Delete(model.AdminRole{}) | |||||
} else { | |||||
return session.Where("adm_id = ?", id).Delete(model.AdminRole{}) | |||||
} | |||||
} | |||||
func (a AdminRoleDb) BatchAddAdminRoleBySession(session *xorm.Session, mm []*model.AdminRole) (int64, error) { | |||||
affected, err := session.Insert(mm) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
return affected, nil | |||||
} |
@@ -0,0 +1,33 @@ | |||||
package implement | |||||
import ( | |||||
"applet/app/db/dao" | |||||
"applet/app/db/model" | |||||
"applet/app/enum" | |||||
"applet/app/utils/logx" | |||||
"xorm.io/xorm" | |||||
) | |||||
func NewPermissionGroupDb(engine *xorm.Engine) dao.PermissionGroupDao { | |||||
return &PermissionGroupDb{Db: engine} | |||||
} | |||||
type PermissionGroupDb struct { | |||||
Db *xorm.Engine | |||||
} | |||||
func (p PermissionGroupDb) FindPermissionGroup() (*[]model.PermissionGroup, error) { | |||||
var m []model.PermissionGroup | |||||
if err := p.Db.Where("state = ?", enum.PermissionGroupStateForNormal).Asc("id").Find(&m); err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &m, nil | |||||
} | |||||
func (p PermissionGroupDb) FindPermissionGroupV2() (*[]model.PermissionGroup, error) { | |||||
var m []model.PermissionGroup | |||||
if err := p.Db.Asc("id").Find(&m); err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &m, nil | |||||
} |
@@ -0,0 +1,14 @@ | |||||
package implement | |||||
import ( | |||||
"applet/app/db/dao" | |||||
"xorm.io/xorm" | |||||
) | |||||
type PermissionGroupPermissionDb struct { | |||||
Db *xorm.Engine | |||||
} | |||||
func NewPermissionGroupPermissionDb(engine *xorm.Engine) dao.PermissionGroupPermissionDao { | |||||
return &PermissionGroupPermissionDb{Db: engine} | |||||
} |
@@ -0,0 +1,14 @@ | |||||
package implement | |||||
import ( | |||||
"applet/app/db/dao" | |||||
"xorm.io/xorm" | |||||
) | |||||
type PermissionDb struct { | |||||
Db *xorm.Engine | |||||
} | |||||
func NewPermissionDb(engine *xorm.Engine) dao.PermissionDao { | |||||
return &PermissionDb{Db: engine} | |||||
} |
@@ -0,0 +1,73 @@ | |||||
package implement | |||||
import ( | |||||
"applet/app/db/dao" | |||||
"applet/app/db/model" | |||||
"applet/app/utils/logx" | |||||
"reflect" | |||||
"xorm.io/xorm" | |||||
) | |||||
func NewRoleDb(engine *xorm.Engine, id int) dao.RoleDao { | |||||
return &RoleDb{ | |||||
Db: engine, | |||||
Id: id, | |||||
} | |||||
} | |||||
type RoleDb struct { | |||||
Db *xorm.Engine | |||||
Id int `json:"id"` | |||||
} | |||||
func (r RoleDb) FindRole() (*[]model.Role, error) { | |||||
var m []model.Role | |||||
if err := r.Db.Desc("id").Find(&m); err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
return &m, nil | |||||
} | |||||
func (r RoleDb) GetRole() (m *model.Role, err error) { | |||||
m = new(model.Role) | |||||
has, err := r.Db.Where("id = ?", r.Id).Get(m) | |||||
if err != nil { | |||||
return nil, logx.Error(err) | |||||
} | |||||
if has == false { | |||||
return nil, nil | |||||
} | |||||
return m, nil | |||||
} | |||||
func (r RoleDb) UpdateRole(m *model.Role, columns ...string) (int64, error) { | |||||
affected, err := r.Db.Where("id =?", m.Id).Cols(columns...).Update(m) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
return affected, nil | |||||
} | |||||
func (r RoleDb) RoleDeleteBySession(session *xorm.Session, id interface{}) (int64, error) { | |||||
if reflect.TypeOf(id).Kind() == reflect.Slice { | |||||
return session.In("id", id).Delete(model.Role{}) | |||||
} else { | |||||
return session.Where("id = ?", id).Delete(model.Role{}) | |||||
} | |||||
} | |||||
func (r RoleDb) RoleInsert(m *model.Role) (int, error) { | |||||
_, err := r.Db.InsertOne(m) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
return m.Id, nil | |||||
} | |||||
func (r RoleDb) FindPermissionGroupByRole(roleId int) (list []*dao.RolePermissionGroup, total int64, err error) { | |||||
total, err = r.Db.Where("role.id =?", roleId). | |||||
Join("LEFT", "role_permission_group", "role_permission_group.role_id = role.id"). | |||||
Join("LEFT", "permission_group", "permission_group.id = role_permission_group.group_id"). | |||||
FindAndCount(&list) | |||||
return | |||||
} |
@@ -0,0 +1,32 @@ | |||||
package implement | |||||
import ( | |||||
"applet/app/db/dao" | |||||
"applet/app/db/model" | |||||
"reflect" | |||||
"xorm.io/xorm" | |||||
) | |||||
func NewRolePermissionGroupDb(engine *xorm.Engine) dao.RolePermissionGroupDao { | |||||
return &RolePermissionGroupDb{Db: engine} | |||||
} | |||||
type RolePermissionGroupDb struct { | |||||
Db *xorm.Engine | |||||
} | |||||
func (r RolePermissionGroupDb) RolePermissionGroupDeleteForRoleBySession(session *xorm.Session, roleId interface{}) (int64, error) { | |||||
if reflect.TypeOf(roleId).Kind() == reflect.Slice { | |||||
return session.In("role_id", roleId).Delete(model.RolePermissionGroup{}) | |||||
} else { | |||||
return session.Where("role_id = ?", roleId).Delete(model.RolePermissionGroup{}) | |||||
} | |||||
} | |||||
func (r RolePermissionGroupDb) BatchAddRolePermissionGroupBySession(session *xorm.Session, mm []*model.RolePermissionGroup) (int64, error) { | |||||
affected, err := session.Insert(mm) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
return affected, nil | |||||
} |
@@ -0,0 +1,14 @@ | |||||
package model | |||||
type Admin struct { | |||||
AdmId int `json:"adm_id" xorm:"not null comment('管理员id') INT(11)"` | |||||
Username string `json:"username" xorm:"not null default '' comment('用户名') VARCHAR(255)"` | |||||
Password string `json:"password" xorm:"not null default '' comment('密码') VARCHAR(255)"` | |||||
State int `json:"state" xorm:"not null default 1 comment('状态(1:正常 2:冻结)') TINYINT(1)"` | |||||
IsSuperAdministrator int `json:"is_super_administrator" xorm:"not null default 0 comment('是否为超级管理员(0:否 1:是)') TINYINT(1)"` | |||||
Memo string `json:"memo" xorm:"not null default '' comment('备注信息') VARCHAR(244)"` | |||||
CreateAt string `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
UpdateAt string `json:"update_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
OpenId string `json:"open_id" xorm:"not null default '' comment('微信openid') VARCHAR(255)"` | |||||
UnionId string `json:"union_id" xorm:"not null default '' comment('微信用户id') VARCHAR(255)"` | |||||
} |
@@ -0,0 +1,10 @@ | |||||
package model | |||||
type AdminRole struct { | |||||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||||
AdmId int `json:"adm_id" xorm:"not null default 0 comment('管理员id') INT(11)"` | |||||
RoleId int `json:"role_id" xorm:"not null default 0 comment('角色id') INT(11)"` | |||||
State int `json:"state" xorm:"not null default 1 comment('状态(1:正常 2:冻结)') TINYINT(1)"` | |||||
CreateAt string `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
UpdateAt string `json:"update_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
} |
@@ -0,0 +1,14 @@ | |||||
package model | |||||
type DbMapping struct { | |||||
DbMasterId string `json:"db_master_id" xorm:"not null pk comment('站长id') VARCHAR(16)"` | |||||
DbHost string `json:"db_host" xorm:"not null default '' comment('数据库连接(带port)') VARCHAR(255)"` | |||||
DbUsername string `json:"db_username" xorm:"not null default '' comment('数据库用户名') VARCHAR(255)"` | |||||
DbPassword string `json:"db_password" xorm:"not null default '' comment('数据库用户名密码') VARCHAR(255)"` | |||||
DbName string `json:"db_name" xorm:"not null comment('数据库名') VARCHAR(255)"` | |||||
ExternalMysql string `json:"external_mysql" xorm:"not null default '0' comment('是否外部mysql(0是内部,1是外部)') VARCHAR(255)"` | |||||
IsDev int `json:"is_dev" xorm:"not null default 0 comment('开发库是1,0是生产库') TINYINT(1)"` | |||||
CreatedAt string `json:"created_at" xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') TIMESTAMP"` | |||||
UpdatedAt string `json:"updated_at" xorm:"not null default 'CURRENT_TIMESTAMP' comment('更新时间') TIMESTAMP"` | |||||
DeletedAt int `json:"deleted_at" xorm:"not null default 0 comment('是否已删除') TINYINT(1)"` | |||||
} |
@@ -0,0 +1,10 @@ | |||||
package model | |||||
type Permission struct { | |||||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||||
Name string `json:"name" xorm:"not null default '' comment('名称') VARCHAR(255)"` | |||||
Action string `json:"action" xorm:"not null default '' comment('路由') unique VARCHAR(255)"` | |||||
State int `json:"state" xorm:"not null default 1 comment('状态(1:正常 2:废弃)') TINYINT(1)"` | |||||
CreateAt string `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
UpdateAt string `json:"update_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
} |
@@ -0,0 +1,11 @@ | |||||
package model | |||||
type PermissionGroup struct { | |||||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||||
ParentId int `json:"parent_id" xorm:"not null default 0 comment('父级id') INT(11)"` | |||||
Key string `json:"key" xorm:"not null default '' VARCHAR(255)"` | |||||
Name string `json:"name" xorm:"not null default '' comment('名称') VARCHAR(255)"` | |||||
State int `json:"state" xorm:"not null default 1 comment('状态(1:正常 2:废弃)') TINYINT(1)"` | |||||
CreateAt string `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
UpdateAt string `json:"update_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
} |
@@ -0,0 +1,9 @@ | |||||
package model | |||||
type PermissionGroupPermission struct { | |||||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||||
GroupId int `json:"group_id" xorm:"not null default 0 comment('权限组id') INT(11)"` | |||||
PermissionId int `json:"permission_id" xorm:"not null default 0 comment('权限id') INT(11)"` | |||||
CreateAt string `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
UpdateAt string `json:"update_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
} |
@@ -0,0 +1,10 @@ | |||||
package model | |||||
type Role struct { | |||||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||||
Name string `json:"name" xorm:"not null default '' comment('名称') VARCHAR(255)"` | |||||
State int `json:"state" xorm:"not null default 1 comment('状态(1:正常 2:冻结)') TINYINT(1)"` | |||||
Memo string `json:"memo" xorm:"not null default '' comment('备注') VARCHAR(255)"` | |||||
CreateAt string `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
UpdateAt string `json:"update_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
} |
@@ -0,0 +1,9 @@ | |||||
package model | |||||
type RolePermissionGroup struct { | |||||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||||
RoleId int `json:"role_id" xorm:"not null default 0 comment('角色id') INT(11)"` | |||||
GroupId int `json:"group_id" xorm:"not null default 0 comment('权限组id') INT(11)"` | |||||
CreateAt string `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
UpdateAt string `json:"update_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
} |
@@ -0,0 +1,7 @@ | |||||
package model | |||||
type SysCfg struct { | |||||
Key string `json:"key" xorm:"not null pk comment('键') VARCHAR(127)"` | |||||
Val string `json:"val" xorm:"comment('值') TEXT"` | |||||
Memo string `json:"memo" xorm:"not null default '' comment('备注') VARCHAR(255)"` | |||||
} |
@@ -0,0 +1,5 @@ | |||||
package dao | |||||
type MediumListDao interface { | |||||
//TODO:: You can add specific method definitions here | |||||
} |
@@ -0,0 +1,14 @@ | |||||
package implement | |||||
import ( | |||||
"applet/app/db/super/dao" | |||||
"xorm.io/xorm" | |||||
) | |||||
func NewMediumListDb(engine *xorm.Engine) dao.MediumListDao { | |||||
return &MediumListDb{Db: engine} | |||||
} | |||||
type MediumListDb struct { | |||||
Db *xorm.Engine | |||||
} |
@@ -0,0 +1,25 @@ | |||||
package model | |||||
type MediumList struct { | |||||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||||
Uuid int `json:"uuid" xorm:"not null comment('站长id') index unique(IDX_UUID_TYPE) INT(11)"` | |||||
MediumId int `json:"medium_id" xorm:"not null comment('媒体id') unique(IDX_UUID_TYPE) INT(11)"` | |||||
Kind int `json:"kind" xorm:"not null default 1 comment('类型(1:企业 2:个人)') TINYINT(1)"` | |||||
CompanyName string `json:"company_name" xorm:"not null default '' comment('公司名称') VARCHAR(255)"` | |||||
CompanyAbbreviation string `json:"company_abbreviation" xorm:"not null default '' comment('公司简称') VARCHAR(255)"` | |||||
UnifiedSocialCreditCode string `json:"unified_social_credit_code" xorm:"not null default '' comment('统一社会信用代码') VARCHAR(255)"` | |||||
CertificateType int `json:"certificate_type" xorm:"not null default 1 comment('证件类型') TINYINT(1)"` | |||||
BusinessLicenseImgUrl string `json:"business_license_img_url" xorm:"not null default '' comment('营业执照照片') VARCHAR(255)"` | |||||
LegalRepresentative string `json:"legal_representative" xorm:"not null default '' comment('法定代表人') CHAR(50)"` | |||||
CountryRegionId int `json:"country_region_id" xorm:"not null default 1 comment('国家地区id') TINYINT(3)"` | |||||
CountryRegion string `json:"country_region" xorm:"not null default '' comment('国家地区') CHAR(50)"` | |||||
RegisteredAddressProvinceId int `json:"registered_address_province_id" xorm:"not null default 0 comment('注册地址-省份id') INT(11)"` | |||||
RegisteredAddressCityId int `json:"registered_address_city_id" xorm:"not null default 0 comment('注册地址-市id') INT(11)"` | |||||
RegisteredAddressCountyId int `json:"registered_address_county_id" xorm:"not null default 0 comment('注册地址-县/区id') INT(11)"` | |||||
RegisteredAddress string `json:"registered_address" xorm:"not null default '' comment('注册地址') VARCHAR(255)"` | |||||
BusinessLicenseAddress string `json:"business_license_address" xorm:"not null default '' comment('营业执照地址') VARCHAR(255)"` | |||||
CertificateValidity string `json:"certificate_validity" xorm:"not null default '0000-00-00' comment('证件有效期') CHAR(50)"` | |||||
State int `json:"state" xorm:"not null default 0 comment('状态(0:待审核 1:审核通过 2:审核拒绝)') TINYINT(1)"` | |||||
CreateAt string `json:"create_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
UpdateAt string `json:"update_at" xorm:"not null default 'CURRENT_TIMESTAMP' DATETIME"` | |||||
} |
@@ -0,0 +1,236 @@ | |||||
package e | |||||
const ( | |||||
// 200 因为部分第三方接口不能返回错误头,因此在此定义部分错误 | |||||
ERR_FILE_SAVE = 200001 | |||||
// 400 系列 | |||||
ERR_BAD_REQUEST = 400000 | |||||
ERR_INVALID_ARGS = 400001 | |||||
ERR_API_RESPONSE = 400002 | |||||
ERR_NO_DATA = 400003 | |||||
ERR_MOBILE_NIL = 400004 | |||||
ERR_MOBILE_MATH = 400005 | |||||
ERR_FILE_EXT = 400006 | |||||
ERR_FILE_MAX_SIZE = 400007 | |||||
ERR_SIGN = 400008 | |||||
ERR_PASSWORD_MATH = 400009 | |||||
ERR_PROVIDER_RESPONSE = 400010 | |||||
ERR_AES_ENCODE = 400011 | |||||
ERR_ADMIN_API = 400012 | |||||
ERR_QINIUAPI_RESPONSE = 400013 | |||||
ERR_URL_TURNCHAIN = 400014 | |||||
// 401 未授权 | |||||
ERR_UNAUTHORIZED = 401000 | |||||
ERR_NOT_AUTH = 401001 | |||||
ERR_SMS_AUTH = 401002 | |||||
ERR_TOKEN_AUTH = 401003 | |||||
ERR_TOKEN_FORMAT = 401004 | |||||
ERR_TOKEN_GEN = 401005 | |||||
ERR_CACHE_SET = 401006 | |||||
// 403 禁止 | |||||
ERR_FORBIDEN = 403000 | |||||
ERR_PLATFORM = 403001 | |||||
ERR_MOBILE_EXIST = 403002 | |||||
ERR_USER_NO_EXIST = 403003 | |||||
ERR_MOBILE_NO_EXIST = 403004 | |||||
ERR_FORBIDEN_VALID = 403005 | |||||
ERR_RELATE_ERR = 403006 | |||||
ERR_REPEAT_RELATE = 403007 | |||||
ERR_MOB_FORBIDEN = 403008 | |||||
ERR_MOB_SMS_NO_AVA = 403009 | |||||
ERR_USER_IS_REG = 403010 | |||||
ERR_MASTER_ID = 403011 | |||||
ERR_CASH_OUT_TIME = 403012 | |||||
ERR_CASH_OUT_FEE = 403013 | |||||
ERR_CASH_OUT_USER_NOT_FOUND = 403014 | |||||
ERR_CASH_OUT_FAIL = 403015 | |||||
ERR_CASH_OUT_TIMES = 403016 | |||||
ERR_CASH_OUT_MINI = 403017 | |||||
ERR_CASH_OUT_MUT = 403018 | |||||
ERR_CASH_OUT_NOT_DECIMAL = 403019 | |||||
ERR_CASH_OUT_NOT_DAY_AVA = 403020 | |||||
ERR_USER_LEVEL_PAY_CHECK_TASK_NO_DONE = 403021 | |||||
ERR_USER_LEVEL_PAY_CHECK_NO_CROSS = 403022 | |||||
ERR_USER_LEVEL_ORD_EXP = 403023 | |||||
ERR_IS_BIND_THIRDPARTY = 403024 | |||||
ERR_USER_LEVEL_UPDATE_CHECK_TASK_NO_DONE = 403025 | |||||
ERR_USER_LEVEL_UPDATE_CHECK_NOT_FOUND_ORDER = 403026 | |||||
ERR_USER_LEVEL_UPDATE_REPEAT = 403027 | |||||
ERR_USER_NO_ACTIVE = 403028 | |||||
ERR_USER_IS_BAN = 403029 | |||||
ERR_ALIPAY_SETTING = 403030 | |||||
ERR_ALIPAY_ORDERTYPE = 403031 | |||||
ERR_CLIPBOARD_UNSUP = 403032 | |||||
ERR_SYSUNION_CONFIG = 403033 | |||||
ERR_WECAHT_MINI = 403034 | |||||
ERR_WECAHT_MINI_CACHE = 403035 | |||||
ERR_WECAHT_MINI_DECODE = 403036 | |||||
ERR_WECHAT_MINI_ACCESSTOKEN = 403037 | |||||
ERR_CURRENT_VIP_LEVEL_AUDITING = 403038 | |||||
ERR_LEVEL_RENEW_SHOULD_KEEP_CURRENT = 403039 | |||||
ERR_LEVEL_UPGRADE_APPLY_AUDITTING = 403040 | |||||
ERR_LEVEL_TASK_PAY_TYPE = 403041 | |||||
ERR_BALANCE_NOT_ENOUGH = 403042 | |||||
ERR_ADMIN_PUSH = 403043 | |||||
ERR_PLAN = 403044 | |||||
ERR_MOB_CONFIG = 403045 | |||||
ERR_BAlANCE_PAY_ORDERTYPE = 403046 | |||||
ERR_PHONE_EXISTED = 403047 | |||||
ERR_NOT_RESULT = 403048 | |||||
ERR_REVIEW = 403049 | |||||
ERR_USER_LEVEL_HAS_PAID = 403050 | |||||
ERR_USER_BIND_OWN = 403051 | |||||
ERR_PARENTUID_ERR = 403052 | |||||
ERR_USER_DEL = 403053 | |||||
ERR_SEARCH_ERR = 403054 | |||||
ERR_LEVEL_REACH_TOP = 403055 | |||||
ERR_USER_CHECK_ERR = 403056 | |||||
ERR_PASSWORD_ERR = 403057 | |||||
// 404 | |||||
ERR_USER_NOTFOUND = 404001 | |||||
ERR_SUP_NOTFOUND = 404002 | |||||
ERR_LEVEL_MAP = 404003 | |||||
ERR_MOD_NOTFOUND = 404004 | |||||
ERR_CLIPBOARD_PARSE = 404005 | |||||
ERR_NOT_FAN = 404006 | |||||
ERR_USER_LEVEL = 404007 | |||||
ERR_LACK_PAY_CFG = 404008 | |||||
ERR_NOT_LEVEL_TASK = 404009 | |||||
ERR_ITEM_NOT_FOUND = 404010 | |||||
ERR_WX_CHECKFILE_NOTFOUND = 404011 | |||||
// 429 请求频繁 | |||||
ERR_TOO_MANY_REQUESTS = 429000 | |||||
// 500 系列 | |||||
ERR = 500000 | |||||
ERR_UNMARSHAL = 500001 | |||||
ERR_UNKNOWN = 500002 | |||||
ERR_SMS = 500003 | |||||
ERR_ARKID_REGISTER = 500004 | |||||
ERR_ARKID_WHITELIST = 500005 | |||||
ERR_ARKID_LOGIN = 500006 | |||||
ERR_CFG = 500007 | |||||
ERR_DB_ORM = 500008 | |||||
ERR_CFG_CACHE = 500009 | |||||
ERR_ZHIMENG_CONVERT_ERR = 500010 | |||||
ERR_ALIPAY_ERR = 500011 | |||||
ERR_ALIPAY_ORDER_ERR = 500012 | |||||
ERR_PAY_ERR = 500013 | |||||
ERR_IS_BIND_THIRDOTHER = 500014 | |||||
) | |||||
var MsgFlags = map[int]string{ | |||||
// 200 | |||||
ERR_FILE_SAVE: "文件保存失败", | |||||
// 400 | |||||
ERR_BAD_REQUEST: "请求失败", | |||||
ERR_INVALID_ARGS: "请求参数错误", | |||||
ERR_API_RESPONSE: "API错误", | |||||
ERR_QINIUAPI_RESPONSE: "七牛请求API错误", | |||||
ERR_URL_TURNCHAIN: "转链失败", | |||||
ERR_NO_DATA: "暂无数据", | |||||
ERR_MOBILE_NIL: "电话号码不能为空", | |||||
ERR_MOBILE_MATH: "电话号码输入有误", | |||||
ERR_FILE_MAX_SIZE: "文件上传大小超限", | |||||
ERR_FILE_EXT: "文件类型不支持", | |||||
ERR_SIGN: "签名校验失败", | |||||
ERR_PROVIDER_RESPONSE: "提供商接口错误", | |||||
ERR_AES_ENCODE: "加解密错误", | |||||
ERR_ADMIN_API: "后台接口请求失败", | |||||
// 401 | |||||
ERR_NOT_AUTH: "请登录后操作", | |||||
ERR_SMS_AUTH: "验证码过期或无效", | |||||
ERR_UNAUTHORIZED: "验证用户失败", | |||||
ERR_TOKEN_FORMAT: "Token格式不对", | |||||
ERR_TOKEN_GEN: "生成Token失败", | |||||
ERR_CACHE_SET: "生成缓存失败", | |||||
// 403 | |||||
ERR_FORBIDEN: "禁止访问", | |||||
ERR_PLATFORM: "平台不支持", | |||||
ERR_MOBILE_EXIST: "该号码已注册过", | |||||
ERR_USER_NO_EXIST: "用户没有注册或账号密码不正确", | |||||
ERR_PASSWORD_ERR: "输入两次密码不一致", | |||||
ERR_RELATE_ERR: "推荐人不能是自己的粉丝", | |||||
ERR_PARENTUID_ERR: "推荐人不存在", | |||||
ERR_TOKEN_AUTH: "登录信息失效,请重新登录", | |||||
ERR_MOB_SMS_NO_AVA: "短信余额不足或智盟短信配置失败", | |||||
ERR_USER_IS_REG: "用户已注册", | |||||
ERR_MASTER_ID: "找不到对应站长的数据库", | |||||
ERR_CASH_OUT_TIME: "非可提现时间段", | |||||
ERR_CASH_OUT_USER_NOT_FOUND: "收款账号不存在", | |||||
ERR_CASH_OUT_FAIL: "提现失败", | |||||
ERR_CASH_OUT_FEE: "提现金额必须大于手续费", | |||||
ERR_CASH_OUT_TIMES: "当日提现次数已达上线", | |||||
ERR_CASH_OUT_MINI: "申请提现金额未达到最低金额要求", | |||||
ERR_CASH_OUT_MUT: "申请提现金额未达到整数倍要求", | |||||
ERR_CASH_OUT_NOT_DECIMAL: "提现申请金额只能是整数", | |||||
ERR_CASH_OUT_NOT_DAY_AVA: "不在可提现日期范围内", | |||||
ERR_USER_LEVEL_PAY_CHECK_TASK_NO_DONE: "请先完成其他任务", | |||||
ERR_USER_LEVEL_PAY_CHECK_NO_CROSS: "无法跨越升级", | |||||
ERR_USER_LEVEL_ORD_EXP: "付费订单已失效", | |||||
ERR_IS_BIND_THIRDPARTY: "该用户已经绑定了", | |||||
ERR_IS_BIND_THIRDOTHER: "该账号已经被绑定了", | |||||
ERR_USER_LEVEL_UPDATE_CHECK_TASK_NO_DONE: "请完成指定任务", | |||||
ERR_USER_LEVEL_UPDATE_CHECK_NOT_FOUND_ORDER: "没有找到对应的订单", | |||||
ERR_USER_LEVEL_UPDATE_REPEAT: "不允许重复升级", | |||||
ERR_USER_NO_ACTIVE: "账户没激活", | |||||
ERR_USER_IS_BAN: "账户已被冻结", | |||||
ERR_SYSUNION_CONFIG: "联盟设置错误,请检查配置", | |||||
ERR_WECAHT_MINI: "小程序响应错误,请检查小程序配置", | |||||
ERR_WECAHT_MINI_CACHE: "获取小程序缓存失败", | |||||
ERR_WECAHT_MINI_DECODE: "小程序解密失败", | |||||
ERR_WECHAT_MINI_ACCESSTOKEN: "无法获取accesstoekn", | |||||
ERR_CURRENT_VIP_LEVEL_AUDITING: "当前等级正在审核中", | |||||
ERR_LEVEL_RENEW_SHOULD_KEEP_CURRENT: "续费只能在当前等级续费", | |||||
ERR_LEVEL_UPGRADE_APPLY_AUDITTING: "已有申请正在审核中,暂时不能申请", | |||||
ERR_LEVEL_TASK_PAY_TYPE: "任务付费类型错误", | |||||
ERR_BALANCE_NOT_ENOUGH: "余额不足", | |||||
ERR_ADMIN_PUSH: "后台MOB推送错误", | |||||
ERR_PLAN: "分拥方案出错", | |||||
ERR_MOB_CONFIG: "Mob 配置错误", | |||||
ERR_BAlANCE_PAY_ORDERTYPE: "无效余额支付订单类型", | |||||
ERR_PHONE_EXISTED: "手机号码已存在", | |||||
ERR_NOT_RESULT: "已加载完毕", | |||||
ERR_REVIEW: "审核模板错误", | |||||
ERR_USER_LEVEL_HAS_PAID: "该等级已经付过款", | |||||
// 404 | |||||
ERR_USER_NOTFOUND: "用户不存在", | |||||
ERR_USER_DEL: "账号被删除,如有疑问请联系客服", | |||||
ERR_SUP_NOTFOUND: "上级用户不存在", | |||||
ERR_LEVEL_MAP: "无等级映射关系", | |||||
ERR_MOD_NOTFOUND: "没有找到对应模块", | |||||
ERR_CLIPBOARD_PARSE: "无法解析剪切板内容", | |||||
ERR_NOT_FAN: "没有粉丝", | |||||
ERR_CLIPBOARD_UNSUP: "不支持该平台", | |||||
ERR_USER_LEVEL: "该等级已不存在", | |||||
ERR_LACK_PAY_CFG: "支付配置不完整", | |||||
ERR_NOT_LEVEL_TASK: "等级任务查找错误", | |||||
ERR_ITEM_NOT_FOUND: "找不到对应商品", | |||||
ERR_WX_CHECKFILE_NOTFOUND: "找不到微信校验文件", | |||||
ERR_USER_BIND_OWN: "不能填写自己的邀请码", | |||||
// 429 | |||||
ERR_TOO_MANY_REQUESTS: "请求频繁,请稍后重试", | |||||
// 500 内部错误 | |||||
ERR: "接口错误", | |||||
ERR_SMS: "短信发送出错", | |||||
ERR_CFG: "服务器配置错误", | |||||
ERR_UNMARSHAL: "JSON解码错误", | |||||
ERR_UNKNOWN: "未知错误", | |||||
ERR_ARKID_LOGIN: "登录失败", | |||||
ERR_MOBILE_NO_EXIST: "该用户未设定手机号", | |||||
ERR_FORBIDEN_VALID: "验证码错误", | |||||
ERR_CFG_CACHE: "获取配置缓存失败", | |||||
ERR_DB_ORM: "数据操作失败", | |||||
ERR_REPEAT_RELATE: "重复关联", | |||||
ERR_ZHIMENG_CONVERT_ERR: "智盟转链失败", | |||||
ERR_MOB_FORBIDEN: "Mob调用失败", | |||||
ERR_ALIPAY_ERR: "支付宝参数错误", | |||||
ERR_ALIPAY_SETTING: "请在后台正确配置支付宝", | |||||
ERR_ALIPAY_ORDERTYPE: "无效支付宝订单类型", | |||||
ERR_ALIPAY_ORDER_ERR: "订单创建错误", | |||||
ERR_PAY_ERR: "未找到支付方式", | |||||
ERR_SEARCH_ERR: "暂无该分类商品", | |||||
ERR_LEVEL_REACH_TOP: "已经是最高等级", | |||||
ERR_USER_CHECK_ERR: "校验失败", | |||||
} |
@@ -0,0 +1,72 @@ | |||||
package e | |||||
import ( | |||||
"fmt" | |||||
"path" | |||||
"runtime" | |||||
) | |||||
type E struct { | |||||
Code int // 错误码 | |||||
msg string // 报错代码 | |||||
st string // 堆栈信息 | |||||
} | |||||
func NewErrCode(code int) error { | |||||
if msg, ok := MsgFlags[code]; ok { | |||||
return E{code, msg, stack(3)} | |||||
} | |||||
return E{ERR_UNKNOWN, "unknown", stack(3)} | |||||
} | |||||
func NewErr(code int, msg string) error { | |||||
return E{code, msg, stack(3)} | |||||
} | |||||
func NewErrf(code int, msg string, args ...interface{}) error { | |||||
return E{code, fmt.Sprintf(msg, args), stack(3)} | |||||
} | |||||
func (e E) Error() string { | |||||
return e.msg | |||||
} | |||||
func stack(skip int) string { | |||||
stk := make([]uintptr, 32) | |||||
str := "" | |||||
l := runtime.Callers(skip, stk[:]) | |||||
for i := 0; i < l; i++ { | |||||
f := runtime.FuncForPC(stk[i]) | |||||
name := f.Name() | |||||
file, line := f.FileLine(stk[i]) | |||||
str += fmt.Sprintf("\n%-30s[%s:%d]", name, path.Base(file), line) | |||||
} | |||||
return str | |||||
} | |||||
// ErrorIsAccountBan is 检查这个账号是否被禁用的错误 | |||||
func ErrorIsAccountBan(e error) bool { | |||||
err, ok := e.(E) | |||||
if ok && err.Code == 403029 { | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
// ErrorIsAccountNoActive is 检查这个账号是否被禁用的错误 | |||||
func ErrorIsAccountNoActive(e error) bool { | |||||
err, ok := e.(E) | |||||
if ok && err.Code == 403028 { | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
// ErrorIsUserDel is 检查这个账号是否被删除 | |||||
func ErrorIsUserDel(e error) bool { | |||||
err, ok := e.(E) | |||||
if ok && err.Code == 403053 { | |||||
return true | |||||
} | |||||
return false | |||||
} |
@@ -0,0 +1,110 @@ | |||||
package e | |||||
import ( | |||||
"applet/app/utils" | |||||
"encoding/json" | |||||
"net/http" | |||||
"github.com/gin-gonic/gin" | |||||
"applet/app/utils/logx" | |||||
) | |||||
// GetMsg get error information based on Code | |||||
// 因为这里code是自己控制的, 因此没考虑报错信息 | |||||
func GetMsg(code int) (int, string) { | |||||
if msg, ok := MsgFlags[code]; ok { | |||||
return code / 1000, msg | |||||
} | |||||
if http.StatusText(code) == "" { | |||||
code = 200 | |||||
} | |||||
return code, MsgFlags[ERR_BAD_REQUEST] | |||||
} | |||||
// 成功输出, fields 是额外字段, 与code, msg同级 | |||||
func OutSuc(c *gin.Context, data interface{}, fields map[string]interface{}) { | |||||
res := gin.H{ | |||||
"code": 1, | |||||
"msg": "ok", | |||||
"data": data, | |||||
} | |||||
if fields != nil { | |||||
for k, v := range fields { | |||||
res[k] = v | |||||
} | |||||
} | |||||
if utils.GetApiVersion(c) > 0 { //加了签名校验只返回加密的字符串 | |||||
jsonData, _ := json.Marshal(res) | |||||
str := utils.ResultAes(c, jsonData) | |||||
c.Writer.WriteString(str) | |||||
} else { | |||||
c.AbortWithStatusJSON(200, res) | |||||
} | |||||
} | |||||
func OutSucPure(c *gin.Context, data interface{}, fields map[string]interface{}) { | |||||
res := gin.H{ | |||||
"code": 1, | |||||
"msg": "ok", | |||||
"data": data, | |||||
} | |||||
if fields != nil { | |||||
for k, v := range fields { | |||||
res[k] = v | |||||
} | |||||
} | |||||
c.Abort() | |||||
c.PureJSON(200, res) | |||||
} | |||||
// 错误输出 | |||||
func OutErr(c *gin.Context, code int, err ...interface{}) { | |||||
statusCode, msg := GetMsg(code) | |||||
if len(err) > 0 && err[0] != nil { | |||||
e := err[0] | |||||
switch v := e.(type) { | |||||
case E: | |||||
statusCode = v.Code / 1000 | |||||
msg = v.Error() | |||||
logx.Error(v.msg + ": " + v.st) // 记录堆栈信息 | |||||
case error: | |||||
logx.Error(v) | |||||
break | |||||
case string: | |||||
msg = v | |||||
case int: | |||||
if _, ok := MsgFlags[v]; ok { | |||||
msg = MsgFlags[v] | |||||
} | |||||
} | |||||
} | |||||
if utils.GetApiVersion(c) > 0 { //加了签名校验只返回加密的字符串 | |||||
jsonData, _ := json.Marshal(gin.H{ | |||||
"code": code, | |||||
"msg": msg, | |||||
"data": []struct{}{}, | |||||
}) | |||||
str := utils.ResultAes(c, jsonData) | |||||
if code > 100000 { | |||||
code = int(utils.FloatFormat(float64(code/1000), 0)) | |||||
} | |||||
c.Status(500) | |||||
c.Writer.WriteString(str) | |||||
} else { | |||||
c.AbortWithStatusJSON(statusCode, gin.H{ | |||||
"code": code, | |||||
"msg": msg, | |||||
"data": []struct{}{}, | |||||
}) | |||||
} | |||||
} | |||||
// 重定向 | |||||
func OutRedirect(c *gin.Context, code int, loc string) { | |||||
if code < 301 || code > 308 { | |||||
code = 303 | |||||
} | |||||
c.Redirect(code, loc) | |||||
c.Abort() | |||||
} |
@@ -0,0 +1,8 @@ | |||||
package e | |||||
func SetCache(cacheTime int64) map[string]interface{} { | |||||
if cacheTime == 0 { | |||||
return map[string]interface{}{"cache_time": cacheTime} | |||||
} | |||||
return map[string]interface{}{"cache_time": cacheTime} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
package enum | |||||
type AdminState int32 | |||||
const ( | |||||
AdminStateForNormal = 1 | |||||
AdminStateForFreeze = 2 | |||||
) | |||||
func (gt AdminState) String() string { | |||||
switch gt { | |||||
case AdminStateForNormal: | |||||
return "正常" | |||||
case AdminStateForFreeze: | |||||
return "冻结" | |||||
default: | |||||
return "未知" | |||||
} | |||||
} | |||||
type IsSuperAdministrator int32 | |||||
const ( | |||||
IsSuperAdministratorTure = 1 | |||||
IsSuperAdministratorFalse = 2 | |||||
) | |||||
func (gt IsSuperAdministrator) String() string { | |||||
switch gt { | |||||
case IsSuperAdministratorTure: | |||||
return "超管" | |||||
case IsSuperAdministratorFalse: | |||||
return "非超管" | |||||
default: | |||||
return "未知" | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
package enum | |||||
type PermissionGroupState int32 | |||||
const ( | |||||
PermissionGroupStateForNormal = 1 | |||||
PermissionGroupStateForDiscard = 2 | |||||
) | |||||
func (gt PermissionGroupState) String() string { | |||||
switch gt { | |||||
case PermissionGroupStateForNormal: | |||||
return "正常" | |||||
case PermissionGroupStateForDiscard: | |||||
return "废弃" | |||||
default: | |||||
return "未知" | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
package enum | |||||
type RoleState int32 | |||||
const ( | |||||
RoleStateForNormal = 1 | |||||
RoleStateForFreeze = 2 | |||||
) | |||||
func (gt RoleState) String() string { | |||||
switch gt { | |||||
case RoleStateForNormal: | |||||
return "正常" | |||||
case RoleStateForFreeze: | |||||
return "冻结" | |||||
default: | |||||
return "未知" | |||||
} | |||||
} |
@@ -0,0 +1,23 @@ | |||||
package hdl | |||||
import ( | |||||
"applet/app/e" | |||||
"applet/app/svc" | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
func Demo(c *gin.Context) { | |||||
var args interface{} | |||||
err := c.ShouldBindJSON(&args) | |||||
if err != nil { | |||||
err = svc.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, map[string]interface{}{ | |||||
"args": args, | |||||
}, nil) | |||||
return | |||||
} |
@@ -0,0 +1,60 @@ | |||||
package hdl | |||||
import ( | |||||
"applet/app/db" | |||||
"applet/app/db/implement" | |||||
"applet/app/e" | |||||
"applet/app/lib/validate" | |||||
"applet/app/md" | |||||
"applet/app/svc" | |||||
"applet/app/utils" | |||||
"fmt" | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
// Login 登陆 | |||||
// @Summary 登陆 | |||||
// @Tags ADMIN | |||||
// @Description 登入 | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param req body md2.LoginReq true "用户名密码" | |||||
// @Success 200 {object} md2.LoginResponse "token" | |||||
// @Failure 400 {object} md2.Response "具体错误" | |||||
// @Router /admin/login [post] | |||||
func Login(c *gin.Context) { | |||||
var req md.LoginReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
adminDb := implement.NewAdminDb(engine) | |||||
admin, err := adminDb.GetAdminByUserName(req.UserName) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err) | |||||
return | |||||
} | |||||
if admin == nil { | |||||
e.OutErr(c, e.ERR_NO_DATA, "账号不存在!") | |||||
return | |||||
} | |||||
if utils.Md5(req.PassWord) != admin.Password { | |||||
e.OutErr(c, e.ERR_INVALID_ARGS, "密码错误") | |||||
return | |||||
} | |||||
ip := utils.GetIP(c.Request) | |||||
key := fmt.Sprintf(md.JwtTokenKey, ip, utils.AnyToString(admin.AdmId)) | |||||
token, err := svc.HandleLoginToken(key, admin) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, md.LoginResponse{ | |||||
Token: token, | |||||
}, nil) | |||||
return | |||||
} |
@@ -0,0 +1,649 @@ | |||||
package hdl | |||||
import ( | |||||
"applet/app/db" | |||||
"applet/app/db/implement" | |||||
"applet/app/db/model" | |||||
"applet/app/e" | |||||
"applet/app/enum" | |||||
"applet/app/lib/validate" | |||||
"applet/app/md" | |||||
"applet/app/svc" | |||||
"applet/app/utils" | |||||
"github.com/gin-gonic/gin" | |||||
"time" | |||||
) | |||||
// PermissionGroupList | |||||
// @Summary 权限组列表 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 权限组列表 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @param adm_id query string true "管理员id" | |||||
// @Success 200 {string} "具体看返回内容" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/permissionGroupList [GET] | |||||
func PermissionGroupList(c *gin.Context) { | |||||
roleId := c.DefaultQuery("role_id", "") | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
qrcodeWithBatchRecordsDb := implement.NewPermissionGroupDb(engine) | |||||
groupList, err := qrcodeWithBatchRecordsDb.FindPermissionGroup() | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
roleDb := implement.NewRoleDb(engine, utils.StrToInt(roleId)) | |||||
list, _, err := roleDb.FindPermissionGroupByRole(utils.StrToInt(roleId)) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
var isHasPermissionGroupId []string | |||||
for _, v := range list { | |||||
isHasPermissionGroupId = append(isHasPermissionGroupId, utils.IntToStr(v.PermissionGroup.Id)) | |||||
} | |||||
var tempRespMap = map[string]*md.PermissionGroupListResp{} | |||||
var tempRespMapKeys []string | |||||
for _, v := range *groupList { | |||||
isCheck := false | |||||
if utils.InArr(utils.IntToStr(v.Id), isHasPermissionGroupId) { | |||||
isCheck = true | |||||
} | |||||
tempRespMap[utils.IntToStr(v.Id)] = &md.PermissionGroupListResp{ | |||||
Id: v.Id, | |||||
Name: v.Name, | |||||
Key: v.Key, | |||||
State: v.State, | |||||
ParentId: v.ParentId, | |||||
CreateAt: v.CreateAt, | |||||
UpdateAt: v.UpdateAt, | |||||
IsCheck: isCheck, | |||||
} | |||||
tempRespMapKeys = append(tempRespMapKeys, utils.IntToStr(v.Id)) | |||||
} | |||||
for _, v := range tempRespMap { | |||||
if v.ParentId != 0 && tempRespMap[utils.IntToStr(v.ParentId)].ParentId != 0 { | |||||
tempRespMap[utils.IntToStr(v.ParentId)].SubPermissionGroupList = append(tempRespMap[utils.IntToStr(v.ParentId)].SubPermissionGroupList, *v) | |||||
} | |||||
} | |||||
for _, v := range tempRespMap { | |||||
if v.ParentId != 0 && tempRespMap[utils.IntToStr(v.ParentId)].ParentId == 0 { | |||||
tempRespMap[utils.IntToStr(v.ParentId)].SubPermissionGroupList = append(tempRespMap[utils.IntToStr(v.ParentId)].SubPermissionGroupList, *v) | |||||
} | |||||
} | |||||
var resp []*md.PermissionGroupListResp | |||||
for _, v := range tempRespMapKeys { | |||||
if tempRespMap[v].ParentId == 0 { | |||||
resp = append(resp, tempRespMap[v]) | |||||
} | |||||
} | |||||
e.OutSuc(c, map[string]interface{}{ | |||||
"list": resp, | |||||
"state": []map[string]interface{}{ | |||||
{ | |||||
"name": enum.PermissionGroupState(enum.PermissionGroupStateForNormal).String(), | |||||
"value": enum.PermissionGroupStateForNormal, | |||||
}, | |||||
{ | |||||
"name": enum.PermissionGroupState(enum.PermissionGroupStateForDiscard).String(), | |||||
"value": enum.PermissionGroupStateForDiscard, | |||||
}, | |||||
}, | |||||
}, nil) | |||||
return | |||||
} | |||||
// RoleList | |||||
// @Summary 角色列表 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 角色列表 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Success 200 {string} "具体看返回内容" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/roleList [GET] | |||||
func RoleList(c *gin.Context) { | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
roleDb := implement.NewRoleDb(engine, 0) | |||||
roleList, err := roleDb.FindRole() | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
adminRoleDb := implement.NewAdminRoleDb(engine) | |||||
adminDb := implement.NewAdminDb(engine) | |||||
var result []*md.RoleListResp | |||||
for _, v := range *roleList { | |||||
var temp md.RoleListResp | |||||
temp.Data = v | |||||
adminRoles, err1 := adminRoleDb.FindAdminRoleByRoleId(v.Id) | |||||
if err1 != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err1.Error()) | |||||
return | |||||
} | |||||
for _, adminRole := range *adminRoles { | |||||
admin, err2 := adminDb.GetAdmin(adminRole.AdmId) | |||||
if err2 != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err2.Error()) | |||||
return | |||||
} | |||||
temp.AdminList = append(temp.AdminList, struct { | |||||
Name string `json:"name"` | |||||
}{ | |||||
Name: admin.Username, | |||||
}) | |||||
} | |||||
result = append(result, &temp) | |||||
} | |||||
e.OutSuc(c, map[string]interface{}{ | |||||
"list": result, | |||||
"state": []map[string]interface{}{ | |||||
{ | |||||
"name": enum.RoleState(enum.RoleStateForNormal).String(), | |||||
"value": enum.RoleStateForNormal, | |||||
}, | |||||
{ | |||||
"name": enum.RoleState(enum.RoleStateForFreeze).String(), | |||||
"value": enum.RoleStateForFreeze, | |||||
}, | |||||
}, | |||||
}, nil) | |||||
return | |||||
} | |||||
// AddRole | |||||
// @Summary 添加角色 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 添加角色 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.AddRoleReq true "请求参数" | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/addRole [POST] | |||||
func AddRole(c *gin.Context) { | |||||
var req md.AddRoleReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
roleDb := implement.NewRoleDb(engine, 0) | |||||
now := time.Now() | |||||
_, err = roleDb.RoleInsert(&model.Role{ | |||||
Name: req.Name, | |||||
State: enum.RoleStateForNormal, | |||||
Memo: req.Memo, | |||||
CreateAt: now.Format("2006-01-02 15:04:05"), | |||||
UpdateAt: now.Format("2006-01-02 15:04:05"), | |||||
}) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// UpdateRole | |||||
// @Summary 修改角色 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 修改角色 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.UpdateRoleReq true "请求参数" | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/updateRole [POST] | |||||
func UpdateRole(c *gin.Context) { | |||||
var req md.UpdateRoleReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
roleDb := implement.NewRoleDb(engine, req.RoleId) | |||||
role, err := roleDb.GetRole() | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
if role == nil { | |||||
e.OutErr(c, e.ERR_NO_DATA, "未查询到相应记录") | |||||
return | |||||
} | |||||
role.Name = req.Name | |||||
role.Memo = req.Memo | |||||
_, err = roleDb.UpdateRole(role, "name", "memo") | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// RoleBindPermissionGroup | |||||
// @Summary 角色绑定权限组 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 角色绑定权限组 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.RoleBindPermissionGroupReq true "请求参数" | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/roleBindPermissionGroup [POST] | |||||
func RoleBindPermissionGroup(c *gin.Context) { | |||||
var req md.RoleBindPermissionGroupReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
err = svc.RoleBindPermissionGroup(c, req) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// UpdateRoleState | |||||
// @Summary 修改角色状态 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 修改角色状态 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.UpdateRoleStateReq true "请求参数" | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/updateRole [POST] | |||||
func UpdateRoleState(c *gin.Context) { | |||||
var req md.UpdateRoleStateReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
roleDb := implement.NewRoleDb(engine, req.RoleId) | |||||
role, err := roleDb.GetRole() | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
if role == nil { | |||||
e.OutErr(c, e.ERR_NO_DATA, "未查询到相应记录") | |||||
return | |||||
} | |||||
role.State = req.State | |||||
_, err = roleDb.UpdateRole(role, "state") | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// DeleteRole | |||||
// @Summary 删除角色 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 删除角色 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.UpdateRoleStateReq true "请求参数" | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/deleteRole/{$id} [DELETE] | |||||
func DeleteRole(c *gin.Context) { | |||||
id := c.Param("id") | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
roleDb := implement.NewRoleDb(engine, utils.StrToInt(id)) | |||||
role, err := roleDb.GetRole() | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
if role == nil { | |||||
e.OutErr(c, e.ERR_NO_DATA, "未查询到相应记录") | |||||
return | |||||
} | |||||
err = svc.DeleteRole(c, utils.StrToInt(id)) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// AdminList | |||||
// @Summary 管理员列表 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 管理员列表 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.AdminListReq true "请求参数" | |||||
// @Success 200 {string} "具体看返回内容" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/adminList [POST] | |||||
func AdminList(c *gin.Context) { | |||||
var req md.AdminListReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
if req.Limit == 0 { | |||||
req.Limit = 10 | |||||
} | |||||
if req.Page == 0 { | |||||
req.Page = 10 | |||||
} | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
adminDb := implement.NewAdminDb(engine) | |||||
adminList, total, err := adminDb.FindAdmin(req.UserName, req.State, req.Page, req.Limit) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
var result []md.AdminListResp | |||||
for _, v := range adminList { | |||||
permissionGroupList, _, err1 := adminDb.FindAdminRolePermissionGroup(v.AdmId) | |||||
if err1 != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err1.Error()) | |||||
return | |||||
} | |||||
var roleList []string | |||||
for _, v1 := range permissionGroupList { | |||||
roleList = append(roleList, v1.Role.Name) | |||||
} | |||||
result = append(result, md.AdminListResp{ | |||||
AdmId: v.AdmId, | |||||
Username: v.Username, | |||||
State: v.State, | |||||
IsSuperAdministrator: v.IsSuperAdministrator, | |||||
Memo: v.Memo, | |||||
CreateAt: v.CreateAt, | |||||
UpdateAt: v.UpdateAt, | |||||
RoleList: roleList, | |||||
}) | |||||
} | |||||
e.OutSuc(c, map[string]interface{}{ | |||||
"list": result, | |||||
"total": total, | |||||
"state": []map[string]interface{}{ | |||||
{ | |||||
"name": enum.RoleState(enum.RoleStateForNormal).String(), | |||||
"value": enum.RoleStateForNormal, | |||||
}, | |||||
{ | |||||
"name": enum.RoleState(enum.RoleStateForFreeze).String(), | |||||
"value": enum.RoleStateForFreeze, | |||||
}, | |||||
}, | |||||
}, nil) | |||||
return | |||||
} | |||||
// UpdateAdminState | |||||
// @Summary 修改管理员状态 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 修改管理员状态 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.UpdateAdminStateReq true "请求参数" | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/updateAdminState [POST] | |||||
func UpdateAdminState(c *gin.Context) { | |||||
var req md.UpdateAdminStateReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
admDb := implement.NewAdminDb(engine) | |||||
admin, err := admDb.GetAdmin(req.AdmId) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
if admin == nil { | |||||
e.OutErr(c, e.ERR_NO_DATA, "未查询到相应记录") | |||||
return | |||||
} | |||||
admin.State = req.State | |||||
_, err = admDb.UpdateAdmin(admin, "state") | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// UpdateAdmin | |||||
// @Summary 修改管理员信息 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 修改管理员信息 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.UpdateAdminReq true "请求参数" | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/updateAdmin [POST] | |||||
func UpdateAdmin(c *gin.Context) { | |||||
var req md.UpdateAdminReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
admDb := implement.NewAdminDb(engine) | |||||
admin, err := admDb.GetAdmin(req.AdmId) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
if admin == nil { | |||||
e.OutErr(c, e.ERR_NO_DATA, "未查询到相应记录") | |||||
return | |||||
} | |||||
admin.Username = req.Username | |||||
admin.Memo = req.Memo | |||||
admin.Password = utils.Md5(req.Password) | |||||
_, err = admDb.UpdateAdmin(admin, "username", "memo", "password") | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// AddAdmin | |||||
// @Summary 新增管理员 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 新增管理员 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.AddAdminReq true "请求参数" | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/addAdmin [POST] | |||||
func AddAdmin(c *gin.Context) { | |||||
var req md.AddAdminReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
admDb := implement.NewAdminDb(engine) | |||||
isHasAdmin, err := admDb.GetAdminByUserName(req.Username) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
if isHasAdmin != nil { | |||||
e.OutErr(c, e.ERR, "当前用户名已存在,请勿重复添加") | |||||
return | |||||
} | |||||
admId, err := admDb.CreateAdminId() | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
admin := model.Admin{ | |||||
AdmId: admId, | |||||
Username: req.Username, | |||||
Password: utils.Md5(req.Password), | |||||
State: enum.AdminStateForNormal, | |||||
IsSuperAdministrator: 0, | |||||
Memo: req.Memo, | |||||
CreateAt: time.Now().Format("2006-01-02 15:04:05"), | |||||
UpdateAt: time.Now().Format("2006-01-02 15:04:05"), | |||||
} | |||||
_, err = admDb.AdminInsert(&admin) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// DeleteAdmin | |||||
// @Summary 删除管理员 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 删除管理员 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/deleteAdmin/{$adm_id} [DELETE] | |||||
func DeleteAdmin(c *gin.Context) { | |||||
admId := c.Param("adm_id") | |||||
err := svc.AdminDelete(c, []int{utils.StrToInt(admId)}) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// BindAdminRole | |||||
// @Summary 管理员绑定角色 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 管理员绑定角色 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @Param args body md.BindAdminRoleReq true "请求参数" | |||||
// @Success 200 {string} "success" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/bindAdminRole/ [POST] | |||||
func BindAdminRole(c *gin.Context) { | |||||
var req md.BindAdminRoleReq | |||||
err := c.ShouldBindJSON(&req) | |||||
if err != nil { | |||||
err = validate.HandleValidateErr(err) | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
} | |||||
err = svc.BindAdminRole(c, req) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR, err.Error()) | |||||
return | |||||
} | |||||
e.OutSuc(c, "success", nil) | |||||
return | |||||
} | |||||
// AdminInfo | |||||
// @Summary 管理员信息 | |||||
// @Tags ADMIN-权限管理 | |||||
// @Description 管理员信息 | |||||
// @param Authorization header string true "验证参数Bearer和token空格拼接" | |||||
// @Accept json | |||||
// @Produce json | |||||
// @param adm_id query string true "管理员id" | |||||
// @Success 200 {string} "具体看返回内容" | |||||
// @Failure 400 {object} md.Response "具体错误" | |||||
// @Router /admin/role/adminInfo [GET] | |||||
func AdminInfo(c *gin.Context) { | |||||
admId := c.DefaultQuery("adm_id", "") | |||||
engine := db.DBs[svc.GetMasterId(c)] | |||||
admDb := implement.NewAdminDb(engine) | |||||
admin, err := admDb.GetAdmin(utils.StrToInt(admId)) | |||||
if err != nil { | |||||
e.OutErr(c, e.ERR_DB_ORM, err.Error()) | |||||
return | |||||
} | |||||
admin.Password = "" | |||||
e.OutSuc(c, map[string]interface{}{ | |||||
"info": admin, | |||||
"state": []map[string]interface{}{ | |||||
{ | |||||
"name": enum.RoleState(enum.RoleStateForNormal).String(), | |||||
"value": enum.RoleStateForNormal, | |||||
}, | |||||
{ | |||||
"name": enum.RoleState(enum.RoleStateForFreeze).String(), | |||||
"value": enum.RoleStateForFreeze, | |||||
}, | |||||
}, | |||||
}, nil) | |||||
return | |||||
} |
@@ -0,0 +1,262 @@ | |||||
package alipay | |||||
import ( | |||||
"applet/app/cfg" | |||||
"applet/app/pay/md" | |||||
"applet/app/utils/logx" | |||||
"fmt" | |||||
"github.com/iGoogle-ink/gopay" | |||||
"github.com/iGoogle-ink/gopay/alipay" | |||||
) | |||||
// TradeAppPay is 支付宝APP支付 | |||||
// 抖音头条小程序使用APP调起 | |||||
func TradeAppPay(appID, priKey, subject, orderID, amount, notiURL, RSA, PKCS string, paySet *md.PayData) (string, error) { | |||||
//初始化支付宝客户端 | |||||
// appID 是在支付宝申请的APPID | |||||
// priKey 是支付宝私钥 | |||||
// subject 是支付订单的主题 | |||||
// orderID 是智莺这边生成的订单id | |||||
// amount 是付费金额 | |||||
// notiURL 通知地址url | |||||
// passback_params 回调通知参数 | |||||
client := alipay.NewClient(appID, priKey, true) | |||||
client.DebugSwitch = gopay.DebugOn | |||||
//判断密钥的类型 | |||||
rsa_type := alipay.RSA2 | |||||
pkcs_type := alipay.PKCS1 | |||||
if RSA == "1" { | |||||
rsa_type = alipay.RSA | |||||
} | |||||
if PKCS == "1" { | |||||
pkcs_type = alipay.PKCS8 | |||||
} | |||||
if paySet.PayAliUseType == "1" { | |||||
rsa_type = alipay.RSA2 | |||||
pkcs_type = alipay.PKCS8 | |||||
} | |||||
//配置公共参数 | |||||
client.SetCharset("utf-8"). | |||||
SetSignType(rsa_type). | |||||
SetPrivateKeyType(pkcs_type) | |||||
if notiURL != "" { | |||||
client.SetNotifyUrl(notiURL) | |||||
} | |||||
//新支付宝支付 | |||||
if paySet.PayAliUseType == "1" { | |||||
appCertSN, err := alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAppCertSn) | |||||
fmt.Println("-应用-") | |||||
fmt.Println(appCertSN) | |||||
if err != nil { | |||||
fmt.Println(err) | |||||
return "", err | |||||
} | |||||
if appCertSN == "" { | |||||
fmt.Println(err) | |||||
return "", err | |||||
} | |||||
client.SetAppCertSN(appCertSN) | |||||
//aliPayRootCertSN, err := alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAlipayRootCertSn) | |||||
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6" | |||||
client.SetAliPayRootCertSN(aliPayRootCertSN) | |||||
aliPayPublicCertSN, err := alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAlipayrsaPublicKey) | |||||
fmt.Println("-公钥-") | |||||
fmt.Println(aliPayPublicCertSN) | |||||
if err != nil { | |||||
fmt.Println(err) | |||||
return "", err | |||||
} | |||||
if aliPayPublicCertSN == "" { | |||||
fmt.Println(err) | |||||
return "", err | |||||
} | |||||
client.SetAliPayPublicCertSN(aliPayPublicCertSN) | |||||
} | |||||
fmt.Println(client) | |||||
//请求参数 | |||||
body := make(gopay.BodyMap) | |||||
body.Set("subject", subject) | |||||
body.Set("body", subject) | |||||
body.Set("out_trade_no", orderID) | |||||
body.Set("total_amount", amount) | |||||
body.Set("timeout_express", "30m") | |||||
// body.Set("passback_params", orderID) | |||||
//手机APP支付参数请求 | |||||
payParam, err := client.TradeAppPay(body) | |||||
if err != nil { | |||||
return "", logx.Warn(err) | |||||
} | |||||
return payParam, nil | |||||
} | |||||
// TradeAppPay is 支付宝H5支付 | |||||
func TradeWapPay(appID, priKey, subject, orderID, amount, notiURL, RSA, PKCS, page_url string, paySet *md.PayData) (string, error) { | |||||
fmt.Println("notifyURL is:>>>>>>>>>>", notiURL) | |||||
//aliPayPublicKey := "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wn1sU/8Q0rYLlZ6sq3enrPZw2ptp6FecHR2bBFLjJ+sKzepROd0bKddgj+Mr1ffr3Ej78mLdWV8IzLfpXUi945DkrQcOUWLY0MHhYVG2jSs/qzFfpzmtut2Cl2TozYpE84zom9ei06u2AXLMBkU6VpznZl+R4qIgnUfByt3Ix5b3h4Cl6gzXMAB1hJrrrCkq+WvWb3Fy0vmk/DUbJEz8i8mQPff2gsHBE1nMPvHVAMw1GMk9ImB4PxucVek4ZbUzVqxZXphaAgUXFK2FSFU+Q+q1SPvHbUsjtIyL+cLA6H/6ybFF9Ffp27Y14AHPw29+243/SpMisbGcj2KD+evBwIDAQAB" | |||||
privateKey := priKey | |||||
//判断密钥的类型 | |||||
rsa_type := alipay.RSA2 | |||||
pkcs_type := alipay.PKCS1 | |||||
if RSA == "1" { | |||||
rsa_type = alipay.RSA | |||||
} | |||||
if PKCS == "1" { | |||||
pkcs_type = alipay.PKCS8 | |||||
} | |||||
if paySet.PayAliUseType == "1" { | |||||
rsa_type = alipay.RSA2 | |||||
pkcs_type = alipay.PKCS8 | |||||
} | |||||
//初始化支付宝客户端 | |||||
// appId:应用ID | |||||
// privateKey:应用秘钥 | |||||
// isProd:是否是正式环境 | |||||
client := alipay.NewClient(appID, privateKey, true) | |||||
//配置公共参数 | |||||
client.SetCharset("utf-8"). | |||||
SetSignType(rsa_type). | |||||
SetPrivateKeyType(pkcs_type). | |||||
SetReturnUrl(page_url). | |||||
SetNotifyUrl(notiURL) | |||||
//新支付宝支付 | |||||
if paySet.PayAliUseType == "1" { | |||||
appCertSN, err := alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAppCertSn) | |||||
if err != nil { | |||||
fmt.Println("appCertSn_err:>>>>>>>>", err) | |||||
return "", err | |||||
} | |||||
if appCertSN == "" { | |||||
fmt.Println(err) | |||||
return "", err | |||||
} | |||||
client.SetAppCertSN(appCertSN) | |||||
//aliPayRootCertSN, err := alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAlipayRootCertSn) | |||||
//if err != nil { | |||||
// | |||||
// fmt.Println("rootcertsn_err:>>>>>>>>>>", err) | |||||
// fmt.Println("rootcertsn_err:>>>>>>>>>>", cfg.WxappletFilepath.URL) | |||||
// fmt.Println("rootcertsn_err:>>>>>>>>>>", paySet.PayAlipayRootCertSn) | |||||
// return "", err | |||||
//} | |||||
//if aliPayRootCertSN == "" { | |||||
// fmt.Println(err) | |||||
// return "", err | |||||
//} | |||||
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6" | |||||
client.SetAliPayRootCertSN(aliPayRootCertSN) | |||||
aliPayPublicCertSN, err := alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAlipayrsaPublicKey) | |||||
if err != nil { | |||||
fmt.Println("publicCertSn_err:>>>>>>>>>>>", err) | |||||
return "", err | |||||
} | |||||
if aliPayPublicCertSN == "" { | |||||
fmt.Println(err) | |||||
return "", err | |||||
} | |||||
client.SetAliPayPublicCertSN(aliPayPublicCertSN) | |||||
} | |||||
//请求参数 | |||||
body := make(gopay.BodyMap) | |||||
body.Set("subject", subject) | |||||
body.Set("out_trade_no", orderID) | |||||
// quit_url is 用户付款中途退出返回商户网站的地址 | |||||
body.Set("quit_url", notiURL) | |||||
body.Set("total_amount", amount) | |||||
// product_code is 销售产品码,商家和支付宝签约的产品码 | |||||
body.Set("product_code", "QUICK_WAP_WAY") | |||||
//手机网站支付请求 | |||||
payUrl, err := client.TradeWapPay(body) | |||||
if err != nil { | |||||
return "", logx.Warn(err) | |||||
} | |||||
return payUrl, nil | |||||
} | |||||
// TradeAppPay is 支付宝小程序本身支付 | |||||
func TradeCreate(appID, priKey, subject, orderID, amount, notiURL, RSA, PKCS string, paySet *md.PayData) (*alipay.TradeCreateResponse, error) { | |||||
//aliPayPublicKey := "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wn1sU/8Q0rYLlZ6sq3enrPZw2ptp6FecHR2bBFLjJ+sKzepROd0bKddgj+Mr1ffr3Ej78mLdWV8IzLfpXUi945DkrQcOUWLY0MHhYVG2jSs/qzFfpzmtut2Cl2TozYpE84zom9ei06u2AXLMBkU6VpznZl+R4qIgnUfByt3Ix5b3h4Cl6gzXMAB1hJrrrCkq+WvWb3Fy0vmk/DUbJEz8i8mQPff2gsHBE1nMPvHVAMw1GMk9ImB4PxucVek4ZbUzVqxZXphaAgUXFK2FSFU+Q+q1SPvHbUsjtIyL+cLA6H/6ybFF9Ffp27Y14AHPw29+243/SpMisbGcj2KD+evBwIDAQAB" | |||||
privateKey := priKey | |||||
rsa_type := alipay.RSA2 | |||||
pkcs_type := alipay.PKCS1 | |||||
if RSA == "1" { | |||||
rsa_type = alipay.RSA | |||||
} | |||||
if PKCS == "1" { | |||||
pkcs_type = alipay.PKCS8 | |||||
} | |||||
if paySet.PayAliUseType == "1" { | |||||
rsa_type = alipay.RSA2 | |||||
pkcs_type = alipay.PKCS8 | |||||
} | |||||
//初始化支付宝客户端 | |||||
// appId:应用ID | |||||
// privateKey:应用私钥,支持PKCS1和PKCS8 | |||||
// isProd:是否是正式环境 | |||||
client := alipay.NewClient(appID, privateKey, true) | |||||
//配置公共参数 | |||||
client.SetCharset("utf-8"). | |||||
SetSignType(rsa_type). | |||||
SetPrivateKeyType(pkcs_type). | |||||
SetNotifyUrl(notiURL) | |||||
if paySet.PayAliUseType == "1" { | |||||
appCertSN, err := alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAppCertSn) | |||||
if err != nil { | |||||
fmt.Println(err) | |||||
return nil, err | |||||
} | |||||
if appCertSN == "" { | |||||
fmt.Println(err) | |||||
return nil, err | |||||
} | |||||
client.SetAppCertSN(appCertSN) | |||||
//aliPayRootCertSN, err := alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAlipayRootCertSn) | |||||
//if err != nil { | |||||
// fmt.Println(err) | |||||
// return nil, err | |||||
//} | |||||
//if aliPayRootCertSN == "" { | |||||
// fmt.Println(err) | |||||
// return nil, err | |||||
//} | |||||
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6" | |||||
client.SetAliPayRootCertSN(aliPayRootCertSN) | |||||
aliPayPublicCertSN, err := alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAlipayrsaPublicKey) | |||||
if err != nil { | |||||
fmt.Println(err) | |||||
return nil, err | |||||
} | |||||
if aliPayPublicCertSN == "" { | |||||
fmt.Println(err) | |||||
return nil, err | |||||
} | |||||
client.SetAliPayPublicCertSN(aliPayPublicCertSN) | |||||
} | |||||
//请求参数 | |||||
body := make(gopay.BodyMap) | |||||
body.Set("subject", subject) | |||||
// 支付宝小程序支付时 buyer_id 为必传参数,需要提前获取,获取方法如下两种 | |||||
// 1、alipay.SystemOauthToken() 返回取值:rsp.SystemOauthTokenResponse.UserId | |||||
// 2、client.SystemOauthToken() 返回取值:aliRsp.SystemOauthTokenResponse.UserId | |||||
buyer_id, err := client.SystemOauthToken(body) | |||||
if err != nil { | |||||
return nil, logx.Warn(err) | |||||
} | |||||
body.Set("buyer_id", buyer_id) | |||||
body.Set("out_trade_no", orderID) | |||||
body.Set("total_amount", amount) | |||||
//创建订单 | |||||
aliRsp, err := client.TradeCreate(body) | |||||
if err != nil { | |||||
return nil, logx.Warn(err) | |||||
} | |||||
logx.Warn("aliRsp:", *aliRsp) | |||||
logx.Warn("aliRsp.TradeNo:", aliRsp.Response.TradeNo) | |||||
return aliRsp, nil | |||||
} |
@@ -0,0 +1,39 @@ | |||||
package auth | |||||
import ( | |||||
"errors" | |||||
"github.com/dgrijalva/jwt-go" | |||||
"time" | |||||
) | |||||
// GenToken 生成JWT | |||||
func GenToken(admId int, username string) (string, error) { | |||||
// 创建一个我们自己的声明 | |||||
c := JWTUser{ | |||||
AdmId: admId, | |||||
Username: username, | |||||
StandardClaims: jwt.StandardClaims{ | |||||
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间 | |||||
Issuer: "advertisement", // 签发人 | |||||
}, | |||||
} | |||||
// 使用指定的签名方法创建签名对象 | |||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) | |||||
// 使用指定的secret签名并获得完整的编码后的字符串token | |||||
return token.SignedString(Secret) | |||||
} | |||||
// ParseToken 解析JWT | |||||
func ParseToken(tokenString string) (*JWTUser, error) { | |||||
// 解析token | |||||
token, err := jwt.ParseWithClaims(tokenString, &JWTUser{}, func(token *jwt.Token) (i interface{}, err error) { | |||||
return Secret, nil | |||||
}) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if claims, ok := token.Claims.(*JWTUser); ok && token.Valid { // 校验token | |||||
return claims, nil | |||||
} | |||||
return nil, errors.New("invalid token") | |||||
} |
@@ -0,0 +1,19 @@ | |||||
package auth | |||||
import ( | |||||
"time" | |||||
"github.com/dgrijalva/jwt-go" | |||||
) | |||||
// TokenExpireDuration is jwt 过期时间 | |||||
const TokenExpireDuration = time.Hour * 24 | |||||
var Secret = []byte("micro_group_admin") | |||||
// JWTUser 如果想要保存更多信息,都可以添加到这个结构体中 | |||||
type JWTUser struct { | |||||
AdmId int `json:"adm_id"` | |||||
Username string `json:"username"` | |||||
jwt.StandardClaims | |||||
} |
@@ -0,0 +1,16 @@ | |||||
package qiniu | |||||
import ( | |||||
"github.com/qiniu/api.v7/v7/auth" | |||||
"github.com/qiniu/api.v7/v7/storage" | |||||
) | |||||
func BucketCreate() error { | |||||
mac := auth.New(AK, SK) | |||||
cfg := storage.Config{ | |||||
// 是否使用https域名进行资源管理 | |||||
UseHTTPS: false, | |||||
} | |||||
bucketManager := storage.NewBucketManager(mac, &cfg) | |||||
return bucketManager.CreateBucket("", storage.RIDHuanan) | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package qiniu | |||||
import ( | |||||
"github.com/qiniu/api.v7/v7/auth" | |||||
"github.com/qiniu/api.v7/v7/storage" | |||||
) | |||||
func BucketDelete(bucketName string) error { | |||||
mac := auth.New(AK, SK) | |||||
cfg := storage.Config{ | |||||
// 是否使用https域名进行资源管理 | |||||
UseHTTPS: false, | |||||
} | |||||
bucketManager := storage.NewBucketManager(mac, &cfg) | |||||
return bucketManager.DropBucket(bucketName) | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package qiniu | |||||
import ( | |||||
"github.com/qiniu/api.v7/v7/auth" | |||||
"github.com/qiniu/api.v7/v7/storage" | |||||
) | |||||
func BucketGetDomain(bucketName string) (string, error) { | |||||
mac := auth.New(AK, SK) | |||||
cfg := storage.Config{UseHTTPS: false} | |||||
bucketManager := storage.NewBucketManager(mac, &cfg) | |||||
b, err := bucketManager.ListBucketDomains(bucketName) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
return b[0].Domain, nil | |||||
} |
@@ -0,0 +1,22 @@ | |||||
package qiniu | |||||
import ( | |||||
"applet/app/utils" | |||||
) | |||||
var ( | |||||
AK = "MmxNdai23egjNUHjdzEVaTPdPCIbWzENz9BQuak3" | |||||
SK = "mElaFlM9O16rXp-ihoQdJ9KOH56naKm3MoyQBA59" | |||||
BUCKET = "dev-fnuoos" // 桶子名称 | |||||
BUCKET_SCHEME = "http" | |||||
BUCKET_REGION = "up-z2.qiniup.com" | |||||
Expires uint64 = 3600 | |||||
) | |||||
func Init(ak, sk, bucket, region, scheme string) { | |||||
AK, SK, BUCKET, BUCKET_REGION, BUCKET_SCHEME = ak, sk, bucket, region, scheme | |||||
} | |||||
func Sign(t string) string { | |||||
return utils.Md5(AK + SK + t) | |||||
} |
@@ -0,0 +1,55 @@ | |||||
package qiniu | |||||
import ( | |||||
"time" | |||||
"github.com/qiniu/api.v7/v7/auth/qbox" | |||||
_ "github.com/qiniu/api.v7/v7/conf" | |||||
"github.com/qiniu/api.v7/v7/storage" | |||||
"applet/app/md" | |||||
"applet/app/utils" | |||||
) | |||||
// 请求图片上传地址信息 | |||||
func ReqImgUpload(f *md.FileCallback, callbackUrl string) interface{} { | |||||
if ext := utils.FileExt(f.FileName); ext == "png" || ext == "jpg" || ext == "jpeg" || ext == "gif" || ext == "bmp" || ext == "webp" { | |||||
f.Width = "$(imageInfo.width)" | |||||
f.Height = "$(imageInfo.height)" | |||||
} | |||||
f.Provider = "qiniu" | |||||
f.FileSize = "$(fsize)" | |||||
f.Hash = "$(etag)" | |||||
f.Bucket = "$(bucket)" | |||||
f.Mime = "$(mimeType)" | |||||
f.Time = utils.Int64ToStr(time.Now().Unix()) | |||||
f.Sign = Sign(f.Time) | |||||
putPolicy := storage.PutPolicy{ | |||||
Scope: BUCKET + ":" + f.FileName, // 使用覆盖方式时候必须请求里面有key,否则报错 | |||||
Expires: Expires, | |||||
ForceSaveKey: true, | |||||
SaveKey: f.FileName, | |||||
MimeLimit: "image/*", // 只允许上传图片 | |||||
CallbackURL: callbackUrl, | |||||
CallbackBody: utils.SerializeStr(f), | |||||
CallbackBodyType: "application/json", | |||||
} | |||||
return &struct { | |||||
Method string `json:"method"` | |||||
Key string `json:"key"` | |||||
Host string `json:"host"` | |||||
Token string `json:"token"` | |||||
}{Key: f.FileName, Method: "POST", Host: BUCKET_SCHEME + "://" + BUCKET_REGION, Token: putPolicy.UploadToken(qbox.NewMac(AK, SK))} | |||||
} | |||||
/* | |||||
form表单上传 | |||||
地址 : http://upload-z2.qiniup.com | |||||
header | |||||
- Content-Type : multipart/form-data | |||||
body : | |||||
- key : 文件名 | |||||
- token : 生成token | |||||
- file : 待上传文件 | |||||
*/ |
@@ -0,0 +1,33 @@ | |||||
package validate | |||||
import ( | |||||
"applet/app/e" | |||||
"applet/app/utils" | |||||
"applet/app/utils/logx" | |||||
"encoding/json" | |||||
"fmt" | |||||
"github.com/go-playground/validator/v10" | |||||
) | |||||
func HandleValidateErr(err error) error { | |||||
switch err.(type) { | |||||
case *json.UnmarshalTypeError: | |||||
return e.NewErr(e.ERR_UNMARSHAL, "参数格式错误") | |||||
case validator.ValidationErrors: | |||||
errs := err.(validator.ValidationErrors) | |||||
transMsgMap := errs.Translate(utils.ValidatorTrans) // utils.ValidatorTrans \app\utils\validator_err_trans.go::ValidatorTransInit初始化获得 | |||||
transMsgOne := transMsgMap[GetOneKeyOfMapString(transMsgMap)] | |||||
return e.NewErr(e.ERR_INVALID_ARGS, transMsgOne) | |||||
default: | |||||
_ = logx.Error(err) | |||||
return e.NewErr(e.ERR, fmt.Sprintf("validate request params, err:%v\n", err)) | |||||
} | |||||
} | |||||
// GetOneKeyOfMapString 取出Map的一个key | |||||
func GetOneKeyOfMapString(collection map[string]string) string { | |||||
for k := range collection { | |||||
return k | |||||
} | |||||
return "" | |||||
} |
@@ -0,0 +1,305 @@ | |||||
package wxpay | |||||
import ( | |||||
"applet/app/utils" | |||||
"applet/app/utils/logx" | |||||
"fmt" | |||||
"github.com/iGoogle-ink/gopay" | |||||
"github.com/iGoogle-ink/gopay/pkg/util" | |||||
"github.com/iGoogle-ink/gopay/wechat" | |||||
v3 "github.com/iGoogle-ink/gopay/wechat/v3" | |||||
"strconv" | |||||
"time" | |||||
) | |||||
func NewClient(appId, mchId, apiKey string, isProd bool) *wechat.Client { | |||||
// 初始化微信客户端 | |||||
// appId:应用ID | |||||
// mchId:商户ID | |||||
// apiKey:API秘钥值 | |||||
// isProd:是否是正式环境 | |||||
client := wechat.NewClient(appId, mchId, apiKey, isProd) | |||||
// 打开Debug开关,输出请求日志,默认关闭 | |||||
client.DebugSwitch = gopay.DebugOn | |||||
// 设置国家:不设置默认 中国国内 | |||||
// wechat.China:中国国内 | |||||
// wechat.China2:中国国内备用 | |||||
// wechat.SoutheastAsia:东南亚 | |||||
// wechat.Other:其他国家 | |||||
client.SetCountry(wechat.China) | |||||
// 添加微信证书 Path 路径 | |||||
// certFilePath:apiclient_cert.pem 路径 | |||||
// keyFilePath:apiclient_key.pem 路径 | |||||
// pkcs12FilePath:apiclient_cert.p12 路径 | |||||
// 返回err | |||||
//client.AddCertFilePath() | |||||
// 添加微信证书内容 Content | |||||
// certFileContent:apiclient_cert.pem 内容 | |||||
// keyFileContent:apiclient_key.pem 内容 | |||||
// pkcs12FileContent:apiclient_cert.p12 内容 | |||||
// 返回err | |||||
//client.AddCertFileContent() | |||||
return client | |||||
} | |||||
// TradeAppPay is 微信APP支付 | |||||
func TradeAppPay(client *wechat.Client, subject, orderID, amount, notifyUrl string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
Set("spbill_create_ip", "127.0.0.1"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_App). | |||||
Set("sign_type", wechat.SignType_MD5) | |||||
/*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||||
// 预下单 | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
//if !ok { | |||||
// return nil, errors.New("验签失败") | |||||
//} | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
res := map[string]string{ | |||||
"appid": client.AppId, | |||||
"partnerid": client.MchId, | |||||
"prepayid": wxRsp.PrepayId, | |||||
"sign": paySign, | |||||
"package": "Sign=WXPay", | |||||
"noncestr": wxRsp.NonceStr, | |||||
"timestamp": timeStamp, | |||||
} | |||||
return res, nil | |||||
} | |||||
// TradeAppPay is 微信H5支付 | |||||
func TradeH5Pay(client *wechat.Client, subject, orderID, amount, notifyUrl, ip string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
//Set("spbill_create_ip", "121.196.29.49"). | |||||
Set("spbill_create_ip", ip). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_H5). | |||||
Set("sign_type", wechat.SignType_MD5). | |||||
SetBodyMap("scene_info", func(bm gopay.BodyMap) { | |||||
bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { | |||||
bm.Set("type", "Wap") | |||||
bm.Set("wap_url", "https://www.fumm.cc") | |||||
bm.Set("wap_name", "zyos") | |||||
}) | |||||
}) | |||||
/*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||||
// 预下单 | |||||
fmt.Println(bm) | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
fmt.Println(wxRsp) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
packages := "prepay_id=" + wxRsp.PrepayId | |||||
paySign := wechat.GetH5PaySign(client.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
fmt.Println("paySign===", paySign) | |||||
r := map[string]string{ | |||||
"redirect_url": wxRsp.MwebUrl, | |||||
} | |||||
return r, nil | |||||
} | |||||
// TradeMiniProgPay is 微信小程序支付 ☑️ | |||||
func TradeMiniProgPay(client *wechat.Client, subject, orderID, amount, notifyUrl, openid string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("openid", openid). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
Set("spbill_create_ip", "127.0.0.1"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_Mini). | |||||
Set("sign_type", wechat.SignType_MD5) | |||||
// 预下单 | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
packages := "prepay_id=" + wxRsp.PrepayId | |||||
paySign := wechat.GetMiniPaySign(client.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
res := map[string]string{ | |||||
"appId": client.AppId, | |||||
"paySign": paySign, | |||||
"signType": wechat.SignType_MD5, | |||||
"package": packages, | |||||
"nonceStr": wxRsp.NonceStr, | |||||
"timeStamp": timeStamp, | |||||
} | |||||
return res, nil | |||||
} | |||||
// TradeAppPayV3 is 微信APP支付v3 | |||||
func TradeAppPayV3(client *v3.ClientV3, subject, orderID, amount, notifyUrl string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
amountNew := utils.AnyToFloat64(amount) * 100 | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amountNew). | |||||
Set("spbill_create_ip", "127.0.0.1"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_App). | |||||
Set("sign_type", wechat.SignType_MD5) | |||||
/*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||||
//// 预下单 | |||||
//wxRsp, err := v3.UnifiedOrder(bm) | |||||
//if err != nil { | |||||
// _ = logx.Warn(err) | |||||
// return nil, err | |||||
//} | |||||
//_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||||
//if err != nil { | |||||
// _ = logx.Warn(err) | |||||
// return nil, err | |||||
//} | |||||
////if !ok { | |||||
//// return nil, errors.New("验签失败") | |||||
////} | |||||
//timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
//paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
//res := map[string]string{ | |||||
// "appid": client.AppId, | |||||
// "partnerid": client.MchId, | |||||
// "prepayid": wxRsp.PrepayId, | |||||
// "sign": paySign, | |||||
// "package": "Sign=WXPay", | |||||
// "noncestr": wxRsp.NonceStr, | |||||
// "timestamp": timeStamp, | |||||
//} | |||||
//return res, nil | |||||
return nil, nil | |||||
} | |||||
//// TradeJSAPIPay is 微信JSAPI支付 | |||||
func TradeJSAPIPay(client *wechat.Client, subject, orderID, amount, notifyUrl, openid string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
Set("spbill_create_ip", "121.196.29.49"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_JsApi). | |||||
Set("sign_type", wechat.SignType_MD5). | |||||
Set("openid", openid). | |||||
SetBodyMap("scene_info", func(bm gopay.BodyMap) { | |||||
bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { | |||||
bm.Set("type", "Wap") | |||||
bm.Set("wap_url", "https://www.fumm.cc") | |||||
bm.Set("wap_name", "zyos") | |||||
}) | |||||
}) | |||||
// 预下单 | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
//if !ok { | |||||
// return nil, errors.New("验签失败") | |||||
//} | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
//paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
packages := "prepay_id=" + wxRsp.PrepayId | |||||
paySign := wechat.GetJsapiPaySign(client.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
logx.Info("wxRsp.PrepayId:" + wxRsp.PrepayId) | |||||
logx.Info("wxRsp.PrepayId:" + wxRsp.PrepayId) | |||||
logx.Info("wxRsp.PrepayId:" + openid) | |||||
res := map[string]string{ | |||||
"appid": client.AppId, | |||||
"partnerid": client.MchId, | |||||
"prepayid": wxRsp.PrepayId, | |||||
"sign": paySign, | |||||
"package": "prepay_id=" + wxRsp.PrepayId, | |||||
"noncestr": wxRsp.NonceStr, | |||||
"timestamp": timeStamp, | |||||
} | |||||
return res, nil | |||||
} | |||||
// TradeH5PayV3 is 微信H5支付v3 | |||||
func TradeH5PayV3(client *wechat.Client, subject, orderID, amount, notifyUrl string) (string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
Set("spbill_create_ip", "127.0.0.1"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_App). | |||||
Set("device_info", "WEB"). | |||||
Set("sign_type", wechat.SignType_MD5). | |||||
SetBodyMap("scene_info", func(bm gopay.BodyMap) { | |||||
bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { | |||||
bm.Set("type", "Wap") | |||||
bm.Set("wap_url", "https://www.fumm.cc") | |||||
bm.Set("wap_name", "H5测试支付") | |||||
}) | |||||
}) /*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||||
// 预下单 | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
if err != nil { | |||||
_ = logx.Warn(err) | |||||
return "", err | |||||
} | |||||
// ====APP支付 paySign==== | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
// 获取APP支付的 paySign | |||||
// 注意:package 参数因为是固定值,无需开发者再传入 | |||||
// appId:AppID | |||||
// partnerid:partnerid | |||||
// nonceStr:随机字符串 | |||||
// prepayId:统一下单成功后得到的值 | |||||
// signType:签名方式,务必与统一下单时用的签名方式一致 | |||||
// timeStamp:时间 | |||||
// apiKey:API秘钥值 | |||||
paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
return paySign, nil | |||||
} | |||||
// TradeMiniProgPayV3 is 微信小程序支付v3 | |||||
func TradeMiniProgPayV3(client *v3.ClientV3, subject, orderID, amount, notifyUrl string) (string, error) { | |||||
return "", nil | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package md | |||||
// 缓存key统一管理, %s格式化为masterId | |||||
const ( | |||||
JwtTokenKey = "%s:advertisement_jwt_token:%s" // jwt, 占位符:ip, admin:id | |||||
JwtTokenCacheTime = 3600 * 24 | |||||
AppCfgCacheKey = "%s:cfg_cache:%s" // 占位符: masterId, key的第一个字母 | |||||
UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额) | |||||
AdminRolePermissionCacheTime = 3600 * 24 * 0.5 | |||||
KEY_SYS_CFG_CACHE = "sys_cfg_cache" | |||||
CfgCacheTime = 86400 | |||||
) |
@@ -0,0 +1,10 @@ | |||||
package md | |||||
type LoginReq struct { | |||||
UserName string `json:"username" binding:"required" label:"登录账号"` | |||||
PassWord string `json:"password" binding:"required" label:"登录密码"` | |||||
} | |||||
type LoginResponse struct { | |||||
Token string `json:"token"` | |||||
} |
@@ -0,0 +1,91 @@ | |||||
package md | |||||
import ( | |||||
"applet/app/db/model" | |||||
) | |||||
type RoleListResp struct { | |||||
Data model.Role `json:"data"` | |||||
AdminList []struct { | |||||
Name string `json:"name"` | |||||
} `json:"admin_list"` | |||||
} | |||||
type UpdateRoleStateReq struct { | |||||
RoleId int `json:"role_id" binding:"required" label:"id"` | |||||
State int `json:"state" binding:"required" label:"状态"` | |||||
} | |||||
type AddRoleReq struct { | |||||
Name string `json:"name" binding:"required" label:"名称"` | |||||
Memo string `json:"memo" binding:"required" label:"备注"` | |||||
} | |||||
type UpdateRoleReq struct { | |||||
RoleId int `json:"role_id" binding:"required" label:"id"` | |||||
Name string `json:"name" binding:"required" label:"名称"` | |||||
Memo string `json:"memo" binding:"required" label:"备注"` | |||||
} | |||||
type RoleBindPermissionGroupReq struct { | |||||
RoleId int `json:"role_id" binding:"required" label:"id"` | |||||
PermissionIds []int `json:"permission_ids" label:"权限组id"` | |||||
} | |||||
type PermissionGroupListResp struct { | |||||
Id int `json:"id"` | |||||
Name string `json:"name"` //菜单名称 | |||||
Key string `json:"key"` //唯一标识符 | |||||
State int `json:"state"` | |||||
ParentId int `json:"parent_id"` //父级id,为0则代表没有父级 | |||||
CreateAt string `json:"create_at"` | |||||
UpdateAt string `json:"update_at"` | |||||
IsCheck bool `json:"is_check"` //是否用用 | |||||
SubPermissionGroupList []PermissionGroupListResp `json:"sub_permission_group_list"` //子集菜单 | |||||
} | |||||
type AdminListReq struct { | |||||
Limit int `json:"limit"` | |||||
Page int `json:"page" ` | |||||
UserName string `json:"username"` | |||||
State int `json:"state"` | |||||
} | |||||
type AdminListResp struct { | |||||
AdmId int `json:"adm_id"` | |||||
Username string `json:"username"` | |||||
State int `json:"state"` | |||||
IsSuperAdministrator int `json:"is_super_administrator"` | |||||
Memo string `json:"memo"` | |||||
CreateAt string `json:"create_at"` | |||||
UpdateAt string `json:"update_at"` | |||||
RoleList []string `json:"role_list"` | |||||
} | |||||
type UpdateAdminStateReq struct { | |||||
AdmId int `json:"adm_id" binding:"required" label:"管理员id"` | |||||
State int `json:"state" binding:"required" label:"状态"` | |||||
} | |||||
type AddAdminReq struct { | |||||
Username string `json:"username" binding:"required" label:"名称"` | |||||
Password string `json:"password" binding:"required" label:"密码"` | |||||
Memo string `json:"memo" label:"备注"` | |||||
} | |||||
type UpdateAdminReq struct { | |||||
AdmId int `json:"adm_id" binding:"required" label:"管理员id"` | |||||
Username string `json:"username" binding:"required" label:"名称"` | |||||
Password string `json:"password" binding:"required" label:"密码"` | |||||
Memo string `json:"memo" label:"备注"` | |||||
} | |||||
type BindAdminRoleReq struct { | |||||
AdmId int `json:"adm_id" binding:"required" label:"管理员id"` | |||||
RoleIds []int `json:"role_ids" label:"角色id"` | |||||
} | |||||
type BindAdminWithEnterpriseReq struct { | |||||
AdmId int `json:"adm_id" binding:"required" label:"管理员id"` | |||||
Ids []int `json:"ids" label:"记录id"` | |||||
} |
@@ -0,0 +1,5 @@ | |||||
package md | |||||
var WhiteUri = []string{ | |||||
"/api/admin/comm/getMenuList", | |||||
} |
@@ -0,0 +1,42 @@ | |||||
package md | |||||
import ( | |||||
"regexp" | |||||
"xorm.io/xorm" | |||||
) | |||||
type DbInfo struct { | |||||
User string | |||||
Psw string | |||||
Name string | |||||
Host string | |||||
} | |||||
func SplitDbInfo(eg *xorm.Engine) *DbInfo { | |||||
if eg == nil { | |||||
return &DbInfo{ | |||||
User: "nil", | |||||
Psw: "nil", | |||||
Host: "nil", | |||||
Name: "nil", | |||||
} | |||||
} | |||||
pattern := `(\w+):(.*)@tcp\(([\w\.\-\:\_]+)\)\/(\w+)` | |||||
reg := regexp.MustCompile(pattern).FindStringSubmatch(eg.DataSourceName()) | |||||
if len(reg) < 5 { | |||||
return &DbInfo{ | |||||
User: "unknown", | |||||
Psw: "unknown", | |||||
Host: "unknown", | |||||
Name: "unknown", | |||||
} | |||||
} | |||||
return &DbInfo{ | |||||
User: reg[1], | |||||
Psw: reg[2], | |||||
Host: reg[3], | |||||
Name: reg[4], | |||||
} | |||||
} |
@@ -0,0 +1,31 @@ | |||||
package mw | |||||
import ( | |||||
"time" | |||||
"github.com/gin-gonic/gin" | |||||
"go.uber.org/zap" | |||||
"applet/app/utils/logx" | |||||
) | |||||
// access log | |||||
func AccessLog(c *gin.Context) { | |||||
start := time.Now() | |||||
c.Next() | |||||
cost := time.Since(start) | |||||
logx.Info(c.Request.URL.Path) | |||||
logger := &zap.Logger{} | |||||
logger.Info(c.Request.URL.Path, | |||||
zap.Int("status", c.Writer.Status()), | |||||
zap.String("method", c.Request.Method), | |||||
zap.String("path", c.Request.URL.Path), | |||||
zap.String("query", c.Request.URL.RawQuery), | |||||
zap.String("ip", c.ClientIP()), | |||||
zap.String("user-agent", c.Request.UserAgent()), | |||||
zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()), | |||||
zap.Duration("cost", cost), | |||||
) | |||||
} |
@@ -0,0 +1,26 @@ | |||||
package mw | |||||
import ( | |||||
"applet/app/e" | |||||
"applet/app/svc" | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
// 检查权限, 签名等等 | |||||
func Auth(c *gin.Context) { | |||||
admin, err := svc.CheckUser(c) | |||||
if err != nil { | |||||
switch err.(type) { | |||||
case e.E: | |||||
err1 := err.(e.E) | |||||
e.OutErr(c, err1.Code, err1.Error()) | |||||
return | |||||
default: | |||||
e.OutErr(c, e.ERR_TOKEN_AUTH, err.Error()) | |||||
return | |||||
} | |||||
} | |||||
// 将当前请求的username信息保存到请求的上下文c上 | |||||
c.Set("admin", admin) | |||||
c.Next() | |||||
} |
@@ -0,0 +1,30 @@ | |||||
package mw | |||||
import ( | |||||
"errors" | |||||
"net/http" | |||||
"strconv" | |||||
"github.com/afex/hystrix-go/hystrix" | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
// 熔断器, 此组件需要在gin.Recovery中间之前进行调用, 否则可能会导致panic时候, 无法recovery, 正确顺序如下 | |||||
//r.Use(BreakerWrapper) | |||||
//r.Use(gin.Recovery()) | |||||
func Breaker(c *gin.Context) { | |||||
name := c.Request.Method + "-" + c.Request.RequestURI | |||||
hystrix.Do(name, func() error { | |||||
c.Next() | |||||
statusCode := c.Writer.Status() | |||||
if statusCode >= http.StatusInternalServerError { | |||||
return errors.New("status code " + strconv.Itoa(statusCode)) | |||||
} | |||||
return nil | |||||
}, func(e error) error { | |||||
if e == hystrix.ErrCircuitOpen { | |||||
c.String(http.StatusAccepted, "请稍后重试") //todo 修改报错方法 | |||||
} | |||||
return e | |||||
}) | |||||
} |
@@ -0,0 +1,17 @@ | |||||
package mw | |||||
import ( | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
// 修改传过来的头部字段 | |||||
func ChangeHeader(c *gin.Context) { | |||||
appvserison := c.GetHeader("AppVersionName") | |||||
if appvserison == "" { | |||||
appvserison = c.GetHeader("app_version_name") | |||||
} | |||||
if appvserison != "" { | |||||
c.Request.Header.Add("app_version_name", appvserison) | |||||
} | |||||
c.Next() | |||||
} |
@@ -0,0 +1,34 @@ | |||||
package mw | |||||
import ( | |||||
"applet/app/e" | |||||
"applet/app/utils" | |||||
"bytes" | |||||
"fmt" | |||||
"github.com/gin-gonic/gin" | |||||
"io/ioutil" | |||||
) | |||||
// CheckSign is 中间件 用来检查签名 | |||||
func CheckSign(c *gin.Context) { | |||||
bools := utils.SignCheck(c) | |||||
if bools == false { | |||||
e.OutErr(c, 400, e.NewErr(400, "签名校验错误,请求失败")) | |||||
return | |||||
} | |||||
c.Next() | |||||
} | |||||
func CheckBody(c *gin.Context) { | |||||
if utils.GetApiVersion(c) > 0 { | |||||
body, _ := ioutil.ReadAll(c.Request.Body) | |||||
fmt.Println(string(body)) | |||||
if string(body) != "" { | |||||
str := utils.ResultAesDecrypt(c, string(body)) | |||||
if str != "" { | |||||
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(str))) | |||||
} | |||||
} | |||||
} | |||||
c.Next() | |||||
} |
@@ -0,0 +1,29 @@ | |||||
package mw | |||||
import ( | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
// cors跨域 | |||||
func Cors(c *gin.Context) { | |||||
// 放行所有OPTIONS方法 | |||||
if c.Request.Method == "OPTIONS" { | |||||
c.AbortWithStatus(204) | |||||
return | |||||
} | |||||
origin := c.Request.Header.Get("Origin") // 请求头部 | |||||
if origin != "" { | |||||
c.Header("Access-Control-Allow-Origin", origin) // 这是允许访问来源域 | |||||
c.Header("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE,UPDATE") // 服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求 | |||||
// header的类型 | |||||
c.Header("Access-Control-Allow-Headers", "Authorization,Content-Length,X-CSRF-Token,Token,session,X_Requested_With,Accept,Origin,Host,Connection,Accept-Encoding,Accept-Language,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Pragma,X-Mx-ReqToken") | |||||
// 允许跨域设置,可以返回其他子段 | |||||
// 跨域关键设置 让浏览器可以解析 | |||||
c.Header("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") | |||||
c.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒 | |||||
c.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true | |||||
c.Set("Content-Type", "Application/json") // 设置返回格式是json | |||||
} | |||||
c.Next() | |||||
} |
@@ -0,0 +1,136 @@ | |||||
package mw | |||||
import ( | |||||
"crypto/sha1" | |||||
"encoding/base64" | |||||
"errors" | |||||
"io" | |||||
"github.com/dchest/uniuri" | |||||
"github.com/gin-contrib/sessions" | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
// csrf,xsrf检查 | |||||
const ( | |||||
csrfSecret = "csrfSecret" | |||||
csrfSalt = "csrfSalt" | |||||
csrfToken = "csrfToken" | |||||
) | |||||
var defaultIgnoreMethods = []string{"GET", "HEAD", "OPTIONS"} | |||||
var defaultErrorFunc = func(c *gin.Context) { | |||||
panic(errors.New("CSRF token mismatch")) | |||||
} | |||||
var defaultTokenGetter = func(c *gin.Context) string { | |||||
r := c.Request | |||||
if t := r.FormValue("_csrf"); len(t) > 0 { | |||||
return t | |||||
} else if t := r.URL.Query().Get("_csrf"); len(t) > 0 { | |||||
return t | |||||
} else if t := r.Header.Get("X-CSRF-TOKEN"); len(t) > 0 { | |||||
return t | |||||
} else if t := r.Header.Get("X-XSRF-TOKEN"); len(t) > 0 { | |||||
return t | |||||
} | |||||
return "" | |||||
} | |||||
// Options stores configurations for a CSRF middleware. | |||||
type Options struct { | |||||
Secret string | |||||
IgnoreMethods []string | |||||
ErrorFunc gin.HandlerFunc | |||||
TokenGetter func(c *gin.Context) string | |||||
} | |||||
func tokenize(secret, salt string) string { | |||||
h := sha1.New() | |||||
io.WriteString(h, salt+"-"+secret) | |||||
hash := base64.URLEncoding.EncodeToString(h.Sum(nil)) | |||||
return hash | |||||
} | |||||
func inArray(arr []string, value string) bool { | |||||
inarr := false | |||||
for _, v := range arr { | |||||
if v == value { | |||||
inarr = true | |||||
break | |||||
} | |||||
} | |||||
return inarr | |||||
} | |||||
// Middleware validates CSRF token. | |||||
func Middleware(options Options) gin.HandlerFunc { | |||||
ignoreMethods := options.IgnoreMethods | |||||
errorFunc := options.ErrorFunc | |||||
tokenGetter := options.TokenGetter | |||||
if ignoreMethods == nil { | |||||
ignoreMethods = defaultIgnoreMethods | |||||
} | |||||
if errorFunc == nil { | |||||
errorFunc = defaultErrorFunc | |||||
} | |||||
if tokenGetter == nil { | |||||
tokenGetter = defaultTokenGetter | |||||
} | |||||
return func(c *gin.Context) { | |||||
session := sessions.Default(c) | |||||
c.Set(csrfSecret, options.Secret) | |||||
if inArray(ignoreMethods, c.Request.Method) { | |||||
c.Next() | |||||
return | |||||
} | |||||
salt, ok := session.Get(csrfSalt).(string) | |||||
if !ok || len(salt) == 0 { | |||||
errorFunc(c) | |||||
return | |||||
} | |||||
token := tokenGetter(c) | |||||
if tokenize(options.Secret, salt) != token { | |||||
errorFunc(c) | |||||
return | |||||
} | |||||
c.Next() | |||||
} | |||||
} | |||||
// GetToken returns a CSRF token. | |||||
func GetToken(c *gin.Context) string { | |||||
session := sessions.Default(c) | |||||
secret := c.MustGet(csrfSecret).(string) | |||||
if t, ok := c.Get(csrfToken); ok { | |||||
return t.(string) | |||||
} | |||||
salt, ok := session.Get(csrfSalt).(string) | |||||
if !ok { | |||||
salt = uniuri.New() | |||||
session.Set(csrfSalt, salt) | |||||
session.Save() | |||||
} | |||||
token := tokenize(secret, salt) | |||||
c.Set(csrfToken, token) | |||||
return token | |||||
} |
@@ -0,0 +1,30 @@ | |||||
package mw | |||||
import ( | |||||
"applet/app/db" | |||||
"applet/app/e" | |||||
"applet/app/svc" | |||||
"errors" | |||||
"fmt" | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
// DB is 中间件 用来检查master_id是否有对应的数据库engine | |||||
func DB(c *gin.Context) { | |||||
fmt.Println(c.Request.Header) | |||||
masterID := svc.GetMasterId(c) | |||||
fmt.Println("master_id", masterID) | |||||
if masterID == "" { | |||||
e.OutErr(c, e.ERR_MASTER_ID, errors.New("not found master_id")) | |||||
return | |||||
} | |||||
_, ok := db.DBs[masterID] | |||||
if !ok { | |||||
e.OutErr(c, e.ERR_MASTER_ID, errors.New("not found master_id in DBs")) | |||||
return | |||||
} | |||||
fmt.Println("master_id", masterID) | |||||
c.Set("mid", masterID) | |||||
c.Next() | |||||
} |
@@ -0,0 +1,58 @@ | |||||
package mw | |||||
import ( | |||||
"bytes" | |||||
"io/ioutil" | |||||
"github.com/gin-gonic/gin" | |||||
"applet/app/utils" | |||||
"applet/app/utils/cache" | |||||
) | |||||
// 限流器 | |||||
func Limiter(c *gin.Context) { | |||||
limit := 100 // 限流次数 | |||||
ttl := 1 // 限流过期时间 | |||||
ip := c.ClientIP() | |||||
// 读取token或者ip | |||||
token := c.GetHeader("Authorization") | |||||
// 判断是否已经超出限额次数 | |||||
method := c.Request.Method | |||||
host := c.Request.Host | |||||
uri := c.Request.URL.String() | |||||
buf := make([]byte, 2048) | |||||
num, _ := c.Request.Body.Read(buf) | |||||
body := buf[:num] | |||||
// Write body back | |||||
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) | |||||
Md5 := utils.Md5(ip + token + method + host + uri + string(body)) | |||||
if cache.Exists(Md5) { | |||||
c.AbortWithStatusJSON(429, gin.H{ | |||||
"code": 429, | |||||
"msg": "don't repeat the request", | |||||
"data": struct{}{}, | |||||
}) | |||||
return | |||||
} | |||||
// 2s后没返回自动释放 | |||||
go cache.SetEx(Md5, "0", ttl) | |||||
key := "LIMITER_" + ip | |||||
reqs, _ := cache.GetInt(key) | |||||
if reqs >= limit { | |||||
c.AbortWithStatusJSON(429, gin.H{ | |||||
"code": 429, | |||||
"msg": "too many requests", | |||||
"data": struct{}{}, | |||||
}) | |||||
return | |||||
} | |||||
if reqs > 0 { | |||||
go cache.Incr(key) | |||||
} else { | |||||
go cache.SetEx(key, 1, ttl) | |||||
} | |||||
c.Next() | |||||
go cache.Del(Md5) | |||||
} |
@@ -0,0 +1,57 @@ | |||||
package mw | |||||
import ( | |||||
"net" | |||||
"net/http" | |||||
"net/http/httputil" | |||||
"os" | |||||
"runtime/debug" | |||||
"strings" | |||||
"github.com/gin-gonic/gin" | |||||
"go.uber.org/zap" | |||||
) | |||||
func Recovery(logger *zap.Logger, stack bool) gin.HandlerFunc { | |||||
return func(c *gin.Context) { | |||||
defer func() { | |||||
if err := recover(); err != nil { | |||||
var brokenPipe bool | |||||
if ne, ok := err.(*net.OpError); ok { | |||||
if se, ok := ne.Err.(*os.SyscallError); ok { | |||||
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { | |||||
brokenPipe = true | |||||
} | |||||
} | |||||
} | |||||
httpRequest, _ := httputil.DumpRequest(c.Request, false) | |||||
if brokenPipe { | |||||
logger.Error(c.Request.URL.Path, | |||||
zap.Any("error", err), | |||||
zap.String("request", string(httpRequest)), | |||||
) | |||||
// If the connection is dead, we can't write a status to it. | |||||
c.Error(err.(error)) | |||||
c.Abort() | |||||
return | |||||
} | |||||
if stack { | |||||
logger.Error("[Recovery from panic]", | |||||
zap.Any("error", err), | |||||
zap.String("request", string(httpRequest)), | |||||
zap.String("stack", string(debug.Stack())), | |||||
) | |||||
} else { | |||||
logger.Error("[Recovery from panic]", | |||||
zap.Any("error", err), | |||||
zap.String("request", string(httpRequest)), | |||||
) | |||||
} | |||||
c.AbortWithStatus(http.StatusInternalServerError) | |||||
} | |||||
}() | |||||
c.Next() | |||||
} | |||||
} |
@@ -0,0 +1,74 @@ | |||||
package router | |||||
import ( | |||||
"applet/app/cfg" | |||||
"applet/app/hdl" | |||||
"applet/app/mw" | |||||
_ "applet/docs" | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
// 初始化路由 | |||||
func Init() *gin.Engine { | |||||
// debug, release, test 项目阶段 | |||||
mode := "release" | |||||
if cfg.Debug { | |||||
mode = "debug" | |||||
} | |||||
gin.SetMode(mode) | |||||
//创建一个新的启动器 | |||||
r := gin.New() | |||||
r.Use(mw.ChangeHeader) | |||||
// 是否打印访问日志, 在非正式环境都打印 | |||||
if mode != "release" { | |||||
r.Use(gin.Logger()) | |||||
} | |||||
r.Use(gin.Recovery()) | |||||
// r.Use(mw.Limiter) | |||||
//r.LoadHTMLGlob("static/html/*") | |||||
r.GET("/favicon.ico", func(c *gin.Context) { | |||||
c.Status(204) | |||||
}) | |||||
r.NoRoute(func(c *gin.Context) { | |||||
c.JSON(404, gin.H{"code": 404, "msg": "page not found", "data": []struct{}{}}) | |||||
}) | |||||
r.NoMethod(func(c *gin.Context) { | |||||
c.JSON(405, gin.H{"code": 405, "msg": "method not allowed", "data": []struct{}{}}) | |||||
}) | |||||
r.Use(mw.Cors) | |||||
route(r.Group("/api")) | |||||
return r | |||||
} | |||||
func route(r *gin.RouterGroup) { | |||||
r.GET("/test", hdl.Demo) | |||||
r.Use(mw.DB) // 以下接口需要用到数据库 | |||||
{ | |||||
r.POST("/login", hdl.Login) | |||||
} | |||||
r.Use(mw.CheckBody) //body参数转换 | |||||
r.Use(mw.CheckSign) //签名校验 | |||||
r.Use(mw.Auth) // 以下接口需要JWT验证 | |||||
rRole(r.Group("/role")) //权限管理 | |||||
} | |||||
func rRole(r *gin.RouterGroup) { | |||||
r.GET("/roleList", hdl.RoleList) //角色列表 | |||||
r.POST("/addRole", hdl.AddRole) //角色添加 | |||||
r.POST("/roleBindPermissionGroup", hdl.RoleBindPermissionGroup) //角色绑定权限组 | |||||
r.POST("/updateRoleState", hdl.UpdateRoleState) //修改角色状态 | |||||
r.POST("/updateRole", hdl.UpdateRole) //修改角色状态 | |||||
r.DELETE("/deleteRole/:id", hdl.DeleteRole) //删除角色 | |||||
r.GET("/permissionGroupList", hdl.PermissionGroupList) //权限组列表 | |||||
r.POST("/adminList", hdl.AdminList) //管理员列表 | |||||
r.POST("/updateAdminState", hdl.UpdateAdminState) //修改管理员状态 | |||||
r.POST("/updateAdmin", hdl.UpdateAdmin) //修改管理员信息 | |||||
r.POST("/addAdmin", hdl.AddAdmin) //新增管理员 | |||||
r.DELETE("/deleteAdmin/:adm_id", hdl.DeleteAdmin) //删除管理员 | |||||
r.GET("/adminInfo", hdl.AdminInfo) //获取管理员信息 | |||||
r.POST("/bindAdminRole", hdl.BindAdminRole) //绑定角色 | |||||
} |
@@ -0,0 +1,31 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/db" | |||||
"applet/app/db/implement" | |||||
"github.com/gin-gonic/gin" | |||||
) | |||||
func AdminDelete(c *gin.Context, admIds []int) (err error) { | |||||
engine := db.DBs[GetMasterId(c)] | |||||
session := engine.NewSession() | |||||
defer session.Close() | |||||
session.Begin() | |||||
//1、删除 `admin` | |||||
adminDb := implement.NewAdminDb(engine) | |||||
_, err = adminDb.AdminDeleteBySession(session, admIds) | |||||
if err != nil { | |||||
_ = session.Rollback() | |||||
return | |||||
} | |||||
//2、删除 `admin_role` | |||||
adminRoleDb := implement.NewAdminRoleDb(engine) | |||||
_, err = adminRoleDb.AdminDeleteBySessionForAdmId(session, admIds) | |||||
if err != nil { | |||||
_ = session.Rollback() | |||||
return | |||||
} | |||||
return session.Commit() | |||||
} |
@@ -0,0 +1,55 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/db" | |||||
"applet/app/db/implement" | |||||
"applet/app/db/model" | |||||
"applet/app/lib/auth" | |||||
"errors" | |||||
"github.com/gin-gonic/gin" | |||||
"strings" | |||||
) | |||||
func GetUser(c *gin.Context) *model.Admin { | |||||
user, _ := c.Get("admin") | |||||
if user == nil { | |||||
return &model.Admin{ | |||||
AdmId: 0, | |||||
Username: "", | |||||
Password: "", | |||||
State: 0, | |||||
IsSuperAdministrator: 0, | |||||
Memo: "", | |||||
CreateAt: "", | |||||
UpdateAt: "", | |||||
OpenId: "", | |||||
UnionId: "", | |||||
} | |||||
} | |||||
return user.(*model.Admin) | |||||
} | |||||
func CheckUser(c *gin.Context) (*model.Admin, error) { | |||||
token := c.GetHeader("Authorization") | |||||
if token == "" { | |||||
return nil, errors.New("token not exist") | |||||
} | |||||
// 按空格分割 | |||||
parts := strings.SplitN(token, " ", 2) | |||||
if !(len(parts) == 2 && parts[0] == "Bearer") { | |||||
return nil, errors.New("token format error") | |||||
} | |||||
// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它 | |||||
mc, err := auth.ParseToken(parts[1]) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
// 获取admin | |||||
adminDb := implement.NewAdminDb(db.DBs[GetMasterId(c)]) | |||||
admin, err := adminDb.GetAdmin(mc.AdmId) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return admin, nil | |||||
} |
@@ -0,0 +1,11 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/db" | |||||
"github.com/gin-gonic/gin" | |||||
"xorm.io/xorm" | |||||
) | |||||
func MasterDb(c *gin.Context) *xorm.Engine { | |||||
return db.DBs[c.GetString("mid")] | |||||
} |
@@ -0,0 +1,33 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/db/model" | |||||
"applet/app/lib/auth" | |||||
"applet/app/md" | |||||
"applet/app/utils/cache" | |||||
"applet/app/utils/logx" | |||||
) | |||||
func HandleLoginToken(cacheKey string, admin *model.Admin) (string, error) { | |||||
// 获取之前生成的token | |||||
token, err := cache.GetString(cacheKey) | |||||
if err != nil { | |||||
_ = logx.Error(err) | |||||
} | |||||
// 没有获取到 | |||||
if err != nil || token == "" { | |||||
// 生成token | |||||
token, err = auth.GenToken(admin.AdmId, admin.Username) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
// 缓存token | |||||
_, err = cache.SetEx(cacheKey, token, md.JwtTokenCacheTime) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
return token, nil | |||||
} | |||||
return token, nil | |||||
} |
@@ -0,0 +1,58 @@ | |||||
package svc | |||||
import ( | |||||
"fmt" | |||||
"github.com/gin-gonic/gin" | |||||
"strconv" | |||||
) | |||||
func GetMasterId(c *gin.Context) (masterId string) { | |||||
masterId = c.GetHeader("master_id") | |||||
if masterId != "" { | |||||
return | |||||
} | |||||
masterId = c.GetString("master_id") | |||||
if masterId == "" { | |||||
//TODO::通过域名查找masterId | |||||
//host := c.Request.Host | |||||
//fmt.Println("not found master_id found MasterId start") | |||||
//masterId = c.GetHeader("MasterId") | |||||
//if masterId == "" && c.GetHeader("Platform") == md.PLATFORM_WAP { // H5 要根据域名去获取mid | |||||
// hostList := strings.Split(c.Request.Host, ".") | |||||
// if isNumeric(hostList[0]) { | |||||
// masterId = hostList[0] | |||||
// } else if isNumeric(hostList[1]) { | |||||
// masterId = hostList[1] | |||||
// } else { | |||||
// // 自定义域名 | |||||
// masterId = svc.GetWebSiteDomainMasterId(baseDb, md.PLATFORM_WAP, c.Request.Host) | |||||
// } | |||||
//} | |||||
//if masterId == "" && c.GetHeader("Platform") == md.PLATFORM_PC { // H5 要根据域名去获取mid | |||||
// hostList := strings.Split(c.Request.Host, ".") | |||||
// if isNumeric(hostList[0]) { | |||||
// masterId = hostList[0] | |||||
// } else if isNumeric(hostList[1]) { | |||||
// masterId = hostList[1] | |||||
// } else { | |||||
// // 自定义域名 | |||||
// masterId = svc.GetWebSiteDomainMasterId(baseDb, md.PLATFORM_PC, c.Request.Host) | |||||
// } | |||||
//} | |||||
//if masterId == "" && c.GetHeader("Platform") == "" { // 无平台访问 | |||||
// hostList := strings.Split(c.Request.Host, ".") | |||||
// if isNumeric(hostList[0]) { | |||||
// masterId = hostList[0] | |||||
// } | |||||
//} | |||||
} | |||||
fmt.Println("master_id:::::::", masterId) | |||||
return | |||||
} | |||||
func isNumeric(str string) bool { | |||||
_, err := strconv.ParseFloat(str, 64) | |||||
return err == nil | |||||
} |
@@ -0,0 +1,85 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/md" | |||||
"applet/app/utils" | |||||
"applet/app/utils/cache" | |||||
"errors" | |||||
"fmt" | |||||
"math/rand" | |||||
"reflect" | |||||
"time" | |||||
) | |||||
const redisMutexLockExpTime = 15 | |||||
// TryGetDistributedLock 分布式锁获取 | |||||
// requestId 用于标识请求客户端,可以是随机字符串,需确保唯一 | |||||
func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool { | |||||
if isNegative { // 多次尝试获取 | |||||
retry := 1 | |||||
for { | |||||
ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") | |||||
// 获取锁成功 | |||||
if err == nil && ok == "OK" { | |||||
return true | |||||
} | |||||
// 尝试多次没获取成功 | |||||
if retry > 10 { | |||||
return false | |||||
} | |||||
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000))) | |||||
retry += 1 | |||||
} | |||||
} else { // 只尝试一次 | |||||
ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX") | |||||
// 获取锁成功 | |||||
if err == nil && ok == "OK" { | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
} | |||||
// ReleaseDistributedLock 释放锁,通过比较requestId,用于确保客户端只释放自己的锁,使用lua脚本保证操作的原子型 | |||||
func ReleaseDistributedLock(lockKey, requestId string) (bool, error) { | |||||
luaScript := ` | |||||
if redis.call("get",KEYS[1]) == ARGV[1] | |||||
then | |||||
return redis.call("del",KEYS[1]) | |||||
else | |||||
return 0 | |||||
end` | |||||
do, err := cache.Do("eval", luaScript, 1, lockKey, requestId) | |||||
fmt.Println(reflect.TypeOf(do)) | |||||
fmt.Println(do) | |||||
if utils.AnyToInt64(do) == 1 { | |||||
return true, err | |||||
} else { | |||||
return false, err | |||||
} | |||||
} | |||||
func GetDistributedLockRequestId(prefix string) string { | |||||
return prefix + utils.IntToStr(rand.Intn(100000000)) | |||||
} | |||||
// HandleBalanceDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁 | |||||
func HandleBalanceDistributedLock(masterId, uid, requestIdPrefix string) (cb func(), err error) { | |||||
// 获取余额更新锁 | |||||
balanceLockKey := fmt.Sprintf(md.UserFinValidUpdateLock, masterId, uid) | |||||
requestId := GetDistributedLockRequestId(requestIdPrefix) | |||||
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) | |||||
if !balanceLockOk { | |||||
return nil, errors.New("系统繁忙,请稍后再试") | |||||
} | |||||
cb = func() { | |||||
_, _ = ReleaseDistributedLock(balanceLockKey, requestId) | |||||
} | |||||
return cb, nil | |||||
} |
@@ -0,0 +1,184 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/db" | |||||
"applet/app/db/implement" | |||||
"applet/app/db/model" | |||||
"applet/app/md" | |||||
"applet/app/utils" | |||||
"applet/app/utils/cache" | |||||
"encoding/json" | |||||
"errors" | |||||
"github.com/gin-gonic/gin" | |||||
"regexp" | |||||
"strings" | |||||
"time" | |||||
) | |||||
func CheckUserRole(c *gin.Context, cacheKey, uri string, admId int) (isHasPermission bool, err error) { | |||||
uri = utils.UriFilterExcludeQueryString(uri) //去除uri中?后的query参数 | |||||
isHasPermission = false | |||||
var rolePermission []string | |||||
var rolePermissionString string | |||||
rolePermissionString, _ = cache.GetString(cacheKey) | |||||
//TODO::判断是否在白名单中 | |||||
if utils.InArr(uri, md.WhiteUri) { | |||||
isHasPermission = true | |||||
return | |||||
} | |||||
if rolePermissionString != "" { | |||||
//if false { | |||||
if err = json.Unmarshal([]byte(rolePermissionString), &rolePermission); err != nil { | |||||
return | |||||
} | |||||
} else { | |||||
adminDb := implement.NewAdminDb(db.DBs[GetMasterId(c)]) | |||||
list, _, err1 := adminDb.GetAdminRolePermission(admId) | |||||
if err1 != nil { | |||||
return isHasPermission, err1 | |||||
} | |||||
for _, v := range list { | |||||
rolePermission = append(rolePermission, v.Permission.Action) | |||||
} | |||||
marshal, err1 := json.Marshal(rolePermission) | |||||
if err1 != nil { | |||||
return isHasPermission, err1 | |||||
} | |||||
rolePermissionString = string(marshal) | |||||
_, err = cache.SetEx(cacheKey, rolePermissionString, md.AdminRolePermissionCacheTime) | |||||
} | |||||
if utils.InArr(uri, rolePermission) { | |||||
isHasPermission = true | |||||
} else { | |||||
//正则匹配占位符情况 | |||||
compileRegex := regexp.MustCompile("[0-9]+") | |||||
matchArr := compileRegex.FindAllString(uri, -1) | |||||
if len(matchArr) > 0 { | |||||
uri = strings.Replace(uri, matchArr[len(matchArr)-1], ":id", 1) | |||||
if utils.InArr(uri, rolePermission) { | |||||
isHasPermission = true | |||||
} | |||||
} | |||||
} | |||||
return | |||||
} | |||||
func DeleteRole(c *gin.Context, roleId int) (err error) { | |||||
session := db.Db.NewSession() | |||||
defer session.Close() | |||||
session.Begin() | |||||
//1、删除 `role` | |||||
roleDb := implement.NewRoleDb(db.DBs[GetMasterId(c)], roleId) | |||||
_, err = roleDb.RoleDeleteBySession(session, roleId) | |||||
if err != nil { | |||||
_ = session.Rollback() | |||||
return | |||||
} | |||||
//2、删除 `role_permission_group` | |||||
rolePermissionGroupDb := implement.NewRolePermissionGroupDb(db.DBs[GetMasterId(c)]) | |||||
_, err = rolePermissionGroupDb.RolePermissionGroupDeleteForRoleBySession(session, roleId) | |||||
if err != nil { | |||||
_ = session.Rollback() | |||||
return | |||||
} | |||||
//3、删除 `admin_role` | |||||
adminRoleDb := implement.NewAdminRoleDb(db.DBs[GetMasterId(c)]) | |||||
_, err = adminRoleDb.AdminRoleDeleteForRoleBySession(session, roleId) | |||||
if err != nil { | |||||
_ = session.Rollback() | |||||
return | |||||
} | |||||
return session.Commit() | |||||
} | |||||
func RoleBindPermissionGroup(c *gin.Context, req md.RoleBindPermissionGroupReq) (err error) { | |||||
session := db.Db.NewSession() | |||||
defer session.Close() | |||||
session.Begin() | |||||
//1、查询 `role` | |||||
roleDb := implement.NewRoleDb(db.DBs[GetMasterId(c)], req.RoleId) | |||||
role, err := roleDb.GetRole() | |||||
if err != nil { | |||||
return | |||||
} | |||||
if role == nil { | |||||
return errors.New("未查询到相应记录") | |||||
} | |||||
//1、删除 `role_permission_group` | |||||
rolePermissionGroupDb := implement.NewRolePermissionGroupDb(db.DBs[GetMasterId(c)]) | |||||
_, err = rolePermissionGroupDb.RolePermissionGroupDeleteForRoleBySession(session, req.RoleId) | |||||
if err != nil { | |||||
_ = session.Rollback() | |||||
return | |||||
} | |||||
//2、新增 `role_permission_group`` | |||||
var mm []*model.RolePermissionGroup | |||||
now := time.Now() | |||||
for _, v := range req.PermissionIds { | |||||
mm = append(mm, &model.RolePermissionGroup{ | |||||
RoleId: role.Id, | |||||
GroupId: v, | |||||
CreateAt: now.Format("2006-01-02 15:04:05"), | |||||
UpdateAt: now.Format("2006-01-02 15:04:05"), | |||||
}) | |||||
} | |||||
_, err = rolePermissionGroupDb.BatchAddRolePermissionGroupBySession(session, mm) | |||||
if err != nil { | |||||
_ = session.Rollback() | |||||
return | |||||
} | |||||
return session.Commit() | |||||
} | |||||
func BindAdminRole(c *gin.Context, req md.BindAdminRoleReq) (err error) { | |||||
session := db.Db.NewSession() | |||||
defer session.Close() | |||||
session.Begin() | |||||
//1、查询 `role` | |||||
adminDb := implement.NewAdminDb(db.DBs[GetMasterId(c)]) | |||||
role, err := adminDb.GetAdmin(req.AdmId) | |||||
if err != nil { | |||||
return | |||||
} | |||||
if role == nil { | |||||
return errors.New("未查询到相应记录") | |||||
} | |||||
//1、删除 `admin_role` | |||||
adminRoleDb := implement.NewAdminRoleDb(db.DBs[GetMasterId(c)]) | |||||
_, err = adminRoleDb.AdminRoleDeleteBySession(session, req.AdmId) | |||||
if err != nil { | |||||
_ = session.Rollback() | |||||
return | |||||
} | |||||
//2、新增 `删除 `admin_role`` | |||||
var mm []*model.AdminRole | |||||
now := time.Now() | |||||
for _, v := range req.RoleIds { | |||||
mm = append(mm, &model.AdminRole{ | |||||
AdmId: req.AdmId, | |||||
RoleId: v, | |||||
State: 1, | |||||
CreateAt: now.Format("2006-01-02 15:04:05"), | |||||
UpdateAt: now.Format("2006-01-02 15:04:05"), | |||||
}) | |||||
} | |||||
_, err = adminRoleDb.BatchAddAdminRoleBySession(session, mm) | |||||
if err != nil { | |||||
_ = session.Rollback() | |||||
return | |||||
} | |||||
return session.Commit() | |||||
} |
@@ -0,0 +1,100 @@ | |||||
package svc | |||||
import ( | |||||
"github.com/gin-gonic/gin" | |||||
"xorm.io/xorm" | |||||
"applet/app/db" | |||||
"applet/app/md" | |||||
"applet/app/utils/cache" | |||||
) | |||||
// 单挑记录获取 | |||||
func SysCfgGet(c *gin.Context, key string) string { | |||||
mid := c.GetString("mid") | |||||
eg := db.DBs[mid] | |||||
return db.SysCfgGetWithDb(eg, mid, key) | |||||
} | |||||
// 多条记录获取 | |||||
func SysCfgFind(c *gin.Context, keys ...string) map[string]string { | |||||
var masterId string | |||||
if c == nil { | |||||
masterId = "" | |||||
} else { | |||||
masterId = c.GetString("mid") | |||||
} | |||||
tmp := SysCfgFindComm(masterId, keys...) | |||||
return tmp | |||||
} | |||||
func SysCfgFindComm(masterId string, keys ...string) map[string]string { | |||||
var eg *xorm.Engine | |||||
if masterId == "" { | |||||
eg = db.Db | |||||
} else { | |||||
eg = db.DBs[masterId] | |||||
} | |||||
res := map[string]string{} | |||||
//TODO::判断keys长度(大于10个直接查数据库) | |||||
if len(keys) > 10 { | |||||
cfgList, _ := db.SysCfgGetAll(eg) | |||||
if cfgList == nil { | |||||
return nil | |||||
} | |||||
for _, v := range *cfgList { | |||||
res[v.Key] = v.Val | |||||
} | |||||
} else { | |||||
for _, key := range keys { | |||||
res[key] = db.SysCfgGetWithDb(eg, masterId, key) | |||||
} | |||||
} | |||||
return res | |||||
} | |||||
// 多条记录获取 | |||||
func EgSysCfgFind(keys ...string) map[string]string { | |||||
var e *xorm.Engine | |||||
res := map[string]string{} | |||||
if len(res) == 0 { | |||||
cfgList, _ := db.SysCfgGetAll(e) | |||||
if cfgList == nil { | |||||
return nil | |||||
} | |||||
for _, v := range *cfgList { | |||||
res[v.Key] = v.Val | |||||
} | |||||
// 先不设置缓存 | |||||
// cache.SetJson(md.KEY_SYS_CFG_CACHE, res, 60) | |||||
} | |||||
if len(keys) == 0 { | |||||
return res | |||||
} | |||||
tmp := map[string]string{} | |||||
for _, v := range keys { | |||||
if val, ok := res[v]; ok { | |||||
tmp[v] = val | |||||
} else { | |||||
tmp[v] = "" | |||||
} | |||||
} | |||||
return tmp | |||||
} | |||||
// 清理系统配置信息 | |||||
func SysCfgCleanCache() { | |||||
cache.Del(md.KEY_SYS_CFG_CACHE) | |||||
} | |||||
// 写入系统设置 | |||||
func SysCfgSet(c *gin.Context, key, val, memo string) bool { | |||||
cfg, err := db.SysCfgGetOne(db.DBs[c.GetString("mid")], key) | |||||
if err != nil || cfg == nil { | |||||
return db.SysCfgInsert(db.DBs[c.GetString("mid")], key, val, memo) | |||||
} | |||||
if memo != "" && cfg.Memo != memo { | |||||
cfg.Memo = memo | |||||
} | |||||
SysCfgCleanCache() | |||||
return db.SysCfgUpdate(db.DBs[c.GetString("mid")], key, val, cfg.Memo) | |||||
} |
@@ -0,0 +1,26 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/e" | |||||
"applet/app/utils" | |||||
"applet/app/utils/logx" | |||||
"encoding/json" | |||||
"fmt" | |||||
"github.com/go-playground/validator/v10" | |||||
) | |||||
// HandleValidateErr 通用请求参数错误处理 | |||||
func HandleValidateErr(err error) error { | |||||
switch err.(type) { | |||||
case *json.UnmarshalTypeError: | |||||
return e.NewErr(e.ERR_UNMARSHAL, "参数格式错误") | |||||
case validator.ValidationErrors: | |||||
errs := err.(validator.ValidationErrors) | |||||
transMsgMap := errs.Translate(utils.ValidatorTrans) | |||||
transMsgOne := transMsgMap[utils.GetOneKeyOfMapString(transMsgMap)] | |||||
return e.NewErr(e.ERR_INVALID_ARGS, transMsgOne) | |||||
default: | |||||
_ = logx.Error(err) | |||||
return e.NewErr(e.ERR, fmt.Sprintf("validate request params, err:%v\n", err)) | |||||
} | |||||
} |
@@ -0,0 +1,117 @@ | |||||
package task | |||||
import ( | |||||
taskMd "applet/app/task/md" | |||||
"time" | |||||
"github.com/robfig/cron/v3" | |||||
"xorm.io/xorm" | |||||
"applet/app/cfg" | |||||
"applet/app/db" | |||||
"applet/app/db/model" | |||||
"applet/app/md" | |||||
"applet/app/utils" | |||||
"applet/app/utils/logx" | |||||
) | |||||
var ( | |||||
timer *cron.Cron | |||||
jobs = map[string]func(*xorm.Engine, string){} | |||||
baseEntryId cron.EntryID | |||||
entryIds []cron.EntryID | |||||
taskCfgList map[string]*[]model.SysCfg | |||||
ch = make(chan int, 30) | |||||
workerNum = 15 // 智盟跟单并发数量 | |||||
otherCh = make(chan int, 30) | |||||
otherWorkerNum = 18 // 淘宝, 苏宁, 考拉并发量 | |||||
) | |||||
func Init() { | |||||
// 初始化任务列表 | |||||
initTasks() | |||||
var err error | |||||
timer = cron.New() | |||||
// reload为初始化数据库方法 | |||||
if baseEntryId, err = timer.AddFunc("@every 15m", reload); err != nil { | |||||
_ = logx.Fatal(err) | |||||
} | |||||
} | |||||
func Run() { | |||||
reload() | |||||
timer.Start() | |||||
_ = logx.Info("auto tasks running...") | |||||
} | |||||
func reload() { | |||||
// 重新初始化数据库 | |||||
db.InitMapDbs(cfg.DB, cfg.Prd) | |||||
if len(taskCfgList) == 0 { | |||||
taskCfgList = map[string]*[]model.SysCfg{} | |||||
} | |||||
// 获取所有站长的配置信息 | |||||
for dbName, v := range db.DBs { | |||||
if conf := db.MapCrontabCfg(v); conf != nil { | |||||
if cfg.Debug { | |||||
dbInfo := md.SplitDbInfo(v) | |||||
// 去掉模版库 | |||||
if dbName == "000000" { | |||||
continue | |||||
} | |||||
_ = logx.Debugf("【MasterId】%s, 【Host】%s, 【Name】%s, 【User】%s, 【prd】%v, 【Task】%v\n", dbName, dbInfo.Host, dbInfo.Name, dbInfo.User, cfg.Prd, utils.SerializeStr(*conf)) | |||||
} | |||||
taskCfgList[dbName] = conf | |||||
} | |||||
} | |||||
if len(taskCfgList) > 0 { | |||||
// 删除原有所有任务 | |||||
if len(entryIds) > 0 { | |||||
for _, v := range entryIds { | |||||
if v != baseEntryId { | |||||
timer.Remove(v) | |||||
} | |||||
} | |||||
entryIds = nil | |||||
} | |||||
var ( | |||||
entryId cron.EntryID | |||||
err error | |||||
) | |||||
// 添加任务 | |||||
for dbName, v := range taskCfgList { | |||||
for _, vv := range *v { | |||||
if _, ok := jobs[vv.Key]; ok && vv.Val != "" { | |||||
// fmt.Println(vv.Val) | |||||
if entryId, err = timer.AddFunc(vv.Val, doTask(dbName, vv.Key)); err == nil { | |||||
entryIds = append(entryIds, entryId) | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func doTask(dbName, fnName string) func() { | |||||
return func() { | |||||
begin := time.Now().Local() | |||||
jobs[fnName](db.DBs[dbName], dbName) | |||||
end := time.Now().Local() | |||||
logx.Infof( | |||||
"[%s] AutoTask <%s> started at <%s>, ended at <%s> duration <%s>", | |||||
dbName, | |||||
fnName, | |||||
begin.Format("2006-01-02 15:04:05.000"), | |||||
end.Format("2006-01-02 15:04:05.000"), | |||||
time.Duration(end.UnixNano()-begin.UnixNano()).String(), | |||||
) | |||||
} | |||||
} | |||||
// 增加自动任务队列 | |||||
func initTasks() { | |||||
jobs[taskMd.MallCronOrderCancel] = taskCancelOrder // 取消订单 | |||||
} |
@@ -0,0 +1,5 @@ | |||||
package md | |||||
const ( | |||||
MallCronOrderCancel = "mall_cron_order_cancel" // 取消订单任务 | |||||
) |
@@ -0,0 +1,64 @@ | |||||
package svc | |||||
import ( | |||||
"applet/app/db" | |||||
"applet/app/utils" | |||||
"applet/app/utils/logx" | |||||
"errors" | |||||
"fmt" | |||||
"time" | |||||
"xorm.io/xorm" | |||||
) | |||||
func CancelOrder(eg *xorm.Engine, dbName string) { | |||||
fmt.Println("cancel order...") | |||||
defer func() { | |||||
if err := recover(); err != nil { | |||||
_ = logx.Error(err) | |||||
} | |||||
}() | |||||
timeStr, err := getCancelCfg(eg, dbName) | |||||
if err != nil { | |||||
fmt.Println(err.Error()) | |||||
return | |||||
} | |||||
now := time.Now() | |||||
// x 分钟后取消订单 | |||||
expTime := now.Add(-time.Hour * time.Duration(utils.StrToInt64(timeStr))) | |||||
expTimeStr := utils.Time2String(expTime, "") | |||||
page := 1 | |||||
for { | |||||
isEmpty, err := handleOnePage(eg, dbName, expTimeStr) | |||||
if err != nil { | |||||
_ = logx.Error(err) | |||||
break | |||||
} | |||||
if isEmpty { | |||||
break | |||||
} | |||||
if page > 100 { | |||||
break | |||||
} | |||||
page += 1 | |||||
} | |||||
} | |||||
func handleOnePage(eg *xorm.Engine, dbName, expTimeStr string) (isEmpty bool, err error) { | |||||
return false, nil | |||||
} | |||||
func getCancelCfg(eg *xorm.Engine, masterId string) (string, error) { | |||||
cfg := db.SysCfgGetWithDb(eg, masterId, "order_expiration_time") | |||||
if cfg == "" { | |||||
return "", errors.New("order_expiration_time no found") | |||||
} | |||||
return cfg, nil | |||||
} |
@@ -0,0 +1,23 @@ | |||||
package task | |||||
import ( | |||||
"applet/app/task/svc" | |||||
"math/rand" | |||||
"time" | |||||
"xorm.io/xorm" | |||||
) | |||||
// 取消订单 | |||||
func taskCancelOrder(eg *xorm.Engine, dbName string) { | |||||
for { | |||||
if len(ch) > workerNum { | |||||
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000))) | |||||
} else { | |||||
goto START | |||||
} | |||||
} | |||||
START: | |||||
ch <- 1 | |||||
svc.CancelOrder(eg, dbName) | |||||
<-ch | |||||
} |
@@ -0,0 +1,123 @@ | |||||
package utils | |||||
import ( | |||||
"bytes" | |||||
"crypto/aes" | |||||
"crypto/cipher" | |||||
"fmt" | |||||
) | |||||
func AesEncrypt(rawData, key []byte) ([]byte, error) { | |||||
block, err := aes.NewCipher(key) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
blockSize := block.BlockSize() | |||||
rawData = PKCS5Padding(rawData, blockSize) | |||||
// rawData = ZeroPadding(rawData, block.BlockSize()) | |||||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) | |||||
encrypted := make([]byte, len(rawData)) | |||||
// 根据CryptBlocks方法的说明,如下方式初始化encrypted也可以 | |||||
// encrypted := rawData | |||||
blockMode.CryptBlocks(encrypted, rawData) | |||||
return encrypted, nil | |||||
} | |||||
func AesDecrypt(encrypted, key []byte) ([]byte, error) { | |||||
block, err := aes.NewCipher(key) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
blockSize := block.BlockSize() | |||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) | |||||
rawData := make([]byte, len(encrypted)) | |||||
// rawData := encrypted | |||||
blockMode.CryptBlocks(rawData, encrypted) | |||||
rawData = PKCS5UnPadding(rawData) | |||||
// rawData = ZeroUnPadding(rawData) | |||||
return rawData, nil | |||||
} | |||||
func ZeroPadding(cipherText []byte, blockSize int) []byte { | |||||
padding := blockSize - len(cipherText)%blockSize | |||||
padText := bytes.Repeat([]byte{0}, padding) | |||||
return append(cipherText, padText...) | |||||
} | |||||
func ZeroUnPadding(rawData []byte) []byte { | |||||
length := len(rawData) | |||||
unPadding := int(rawData[length-1]) | |||||
return rawData[:(length - unPadding)] | |||||
} | |||||
func PKCS5Padding(cipherText []byte, blockSize int) []byte { | |||||
padding := blockSize - len(cipherText)%blockSize | |||||
padText := bytes.Repeat([]byte{byte(padding)}, padding) | |||||
return append(cipherText, padText...) | |||||
} | |||||
func PKCS5UnPadding(rawData []byte) []byte { | |||||
length := len(rawData) | |||||
// 去掉最后一个字节 unPadding 次 | |||||
unPadding := int(rawData[length-1]) | |||||
return rawData[:(length - unPadding)] | |||||
} | |||||
// 填充0 | |||||
func zeroFill(key *string) { | |||||
l := len(*key) | |||||
if l != 16 && l != 24 && l != 32 { | |||||
if l < 16 { | |||||
*key = *key + fmt.Sprintf("%0*d", 16-l, 0) | |||||
} else if l < 24 { | |||||
*key = *key + fmt.Sprintf("%0*d", 24-l, 0) | |||||
} else if l < 32 { | |||||
*key = *key + fmt.Sprintf("%0*d", 32-l, 0) | |||||
} else { | |||||
*key = string([]byte(*key)[:32]) | |||||
} | |||||
} | |||||
} | |||||
type AesCrypt struct { | |||||
Key []byte | |||||
Iv []byte | |||||
} | |||||
func (a *AesCrypt) Encrypt(data []byte) ([]byte, error) { | |||||
aesBlockEncrypt, err := aes.NewCipher(a.Key) | |||||
if err != nil { | |||||
println(err.Error()) | |||||
return nil, err | |||||
} | |||||
content := pKCS5Padding(data, aesBlockEncrypt.BlockSize()) | |||||
cipherBytes := make([]byte, len(content)) | |||||
aesEncrypt := cipher.NewCBCEncrypter(aesBlockEncrypt, a.Iv) | |||||
aesEncrypt.CryptBlocks(cipherBytes, content) | |||||
return cipherBytes, nil | |||||
} | |||||
func (a *AesCrypt) Decrypt(src []byte) (data []byte, err error) { | |||||
decrypted := make([]byte, len(src)) | |||||
var aesBlockDecrypt cipher.Block | |||||
aesBlockDecrypt, err = aes.NewCipher(a.Key) | |||||
if err != nil { | |||||
println(err.Error()) | |||||
return nil, err | |||||
} | |||||
aesDecrypt := cipher.NewCBCDecrypter(aesBlockDecrypt, a.Iv) | |||||
aesDecrypt.CryptBlocks(decrypted, src) | |||||
return pKCS5Trimming(decrypted), nil | |||||
} | |||||
func pKCS5Padding(cipherText []byte, blockSize int) []byte { | |||||
padding := blockSize - len(cipherText)%blockSize | |||||
padText := bytes.Repeat([]byte{byte(padding)}, padding) | |||||
return append(cipherText, padText...) | |||||
} | |||||
func pKCS5Trimming(encrypt []byte) []byte { | |||||
padding := encrypt[len(encrypt)-1] | |||||
return encrypt[:len(encrypt)-int(padding)] | |||||
} |
@@ -0,0 +1,95 @@ | |||||
package utils | |||||
import ( | |||||
"encoding/base64" | |||||
"fmt" | |||||
) | |||||
const ( | |||||
Base64Std = iota | |||||
Base64Url | |||||
Base64RawStd | |||||
Base64RawUrl | |||||
) | |||||
func Base64StdEncode(str interface{}) string { | |||||
return Base64Encode(str, Base64Std) | |||||
} | |||||
func Base64StdDecode(str interface{}) string { | |||||
return Base64Decode(str, Base64Std) | |||||
} | |||||
func Base64UrlEncode(str interface{}) string { | |||||
return Base64Encode(str, Base64Url) | |||||
} | |||||
func Base64UrlDecode(str interface{}) string { | |||||
return Base64Decode(str, Base64Url) | |||||
} | |||||
func Base64RawStdEncode(str interface{}) string { | |||||
return Base64Encode(str, Base64RawStd) | |||||
} | |||||
func Base64RawStdDecode(str interface{}) string { | |||||
return Base64Decode(str, Base64RawStd) | |||||
} | |||||
func Base64RawUrlEncode(str interface{}) string { | |||||
return Base64Encode(str, Base64RawUrl) | |||||
} | |||||
func Base64RawUrlDecode(str interface{}) string { | |||||
return Base64Decode(str, Base64RawUrl) | |||||
} | |||||
func Base64Encode(str interface{}, encode int) string { | |||||
newEncode := base64Encode(encode) | |||||
if newEncode == nil { | |||||
return "" | |||||
} | |||||
switch v := str.(type) { | |||||
case string: | |||||
return newEncode.EncodeToString([]byte(v)) | |||||
case []byte: | |||||
return newEncode.EncodeToString(v) | |||||
} | |||||
return newEncode.EncodeToString([]byte(fmt.Sprint(str))) | |||||
} | |||||
func Base64Decode(str interface{}, encode int) string { | |||||
var err error | |||||
var b []byte | |||||
newEncode := base64Encode(encode) | |||||
if newEncode == nil { | |||||
return "" | |||||
} | |||||
switch v := str.(type) { | |||||
case string: | |||||
b, err = newEncode.DecodeString(v) | |||||
case []byte: | |||||
b, err = newEncode.DecodeString(string(v)) | |||||
default: | |||||
return "" | |||||
} | |||||
if err != nil { | |||||
return "" | |||||
} | |||||
return string(b) | |||||
} | |||||
func base64Encode(encode int) *base64.Encoding { | |||||
switch encode { | |||||
case Base64Std: | |||||
return base64.StdEncoding | |||||
case Base64Url: | |||||
return base64.URLEncoding | |||||
case Base64RawStd: | |||||
return base64.RawStdEncoding | |||||
case Base64RawUrl: | |||||
return base64.RawURLEncoding | |||||
default: | |||||
return nil | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
package utils | |||||
import "reflect" | |||||
// 检验一个值是否为空 | |||||
func Empty(val interface{}) bool { | |||||
v := reflect.ValueOf(val) | |||||
switch v.Kind() { | |||||
case reflect.String, reflect.Array: | |||||
return v.Len() == 0 | |||||
case reflect.Map, reflect.Slice: | |||||
return v.Len() == 0 || v.IsNil() | |||||
case reflect.Bool: | |||||
return !v.Bool() | |||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||||
return v.Int() == 0 | |||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||||
return v.Uint() == 0 | |||||
case reflect.Float32, reflect.Float64: | |||||
return v.Float() == 0 | |||||
case reflect.Interface, reflect.Ptr: | |||||
return v.IsNil() | |||||
} | |||||
return reflect.DeepEqual(val, reflect.Zero(v.Type()).Interface()) | |||||
} |
@@ -0,0 +1,421 @@ | |||||
package cache | |||||
import ( | |||||
"errors" | |||||
"fmt" | |||||
"strconv" | |||||
"time" | |||||
) | |||||
const ( | |||||
redisDialTTL = 10 * time.Second | |||||
redisReadTTL = 3 * time.Second | |||||
redisWriteTTL = 3 * time.Second | |||||
redisIdleTTL = 10 * time.Second | |||||
redisPoolTTL = 10 * time.Second | |||||
redisPoolSize int = 512 | |||||
redisMaxIdleConn int = 64 | |||||
redisMaxActive int = 512 | |||||
) | |||||
var ( | |||||
ErrNil = errors.New("nil return") | |||||
ErrWrongArgsNum = errors.New("args num error") | |||||
ErrNegativeInt = errors.New("redis cluster: unexpected value for Uint64") | |||||
) | |||||
// 以下为提供类型转换 | |||||
func Int(reply interface{}, err error) (int, error) { | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
switch reply := reply.(type) { | |||||
case int: | |||||
return reply, nil | |||||
case int8: | |||||
return int(reply), nil | |||||
case int16: | |||||
return int(reply), nil | |||||
case int32: | |||||
return int(reply), nil | |||||
case int64: | |||||
x := int(reply) | |||||
if int64(x) != reply { | |||||
return 0, strconv.ErrRange | |||||
} | |||||
return x, nil | |||||
case uint: | |||||
n := int(reply) | |||||
if n < 0 { | |||||
return 0, strconv.ErrRange | |||||
} | |||||
return n, nil | |||||
case uint8: | |||||
return int(reply), nil | |||||
case uint16: | |||||
return int(reply), nil | |||||
case uint32: | |||||
n := int(reply) | |||||
if n < 0 { | |||||
return 0, strconv.ErrRange | |||||
} | |||||
return n, nil | |||||
case uint64: | |||||
n := int(reply) | |||||
if n < 0 { | |||||
return 0, strconv.ErrRange | |||||
} | |||||
return n, nil | |||||
case []byte: | |||||
data := string(reply) | |||||
if len(data) == 0 { | |||||
return 0, ErrNil | |||||
} | |||||
n, err := strconv.ParseInt(data, 10, 0) | |||||
return int(n), err | |||||
case string: | |||||
if len(reply) == 0 { | |||||
return 0, ErrNil | |||||
} | |||||
n, err := strconv.ParseInt(reply, 10, 0) | |||||
return int(n), err | |||||
case nil: | |||||
return 0, ErrNil | |||||
case error: | |||||
return 0, reply | |||||
} | |||||
return 0, fmt.Errorf("redis cluster: unexpected type for Int, got type %T", reply) | |||||
} | |||||
func Int64(reply interface{}, err error) (int64, error) { | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
switch reply := reply.(type) { | |||||
case int: | |||||
return int64(reply), nil | |||||
case int8: | |||||
return int64(reply), nil | |||||
case int16: | |||||
return int64(reply), nil | |||||
case int32: | |||||
return int64(reply), nil | |||||
case int64: | |||||
return reply, nil | |||||
case uint: | |||||
n := int64(reply) | |||||
if n < 0 { | |||||
return 0, strconv.ErrRange | |||||
} | |||||
return n, nil | |||||
case uint8: | |||||
return int64(reply), nil | |||||
case uint16: | |||||
return int64(reply), nil | |||||
case uint32: | |||||
return int64(reply), nil | |||||
case uint64: | |||||
n := int64(reply) | |||||
if n < 0 { | |||||
return 0, strconv.ErrRange | |||||
} | |||||
return n, nil | |||||
case []byte: | |||||
data := string(reply) | |||||
if len(data) == 0 { | |||||
return 0, ErrNil | |||||
} | |||||
n, err := strconv.ParseInt(data, 10, 64) | |||||
return n, err | |||||
case string: | |||||
if len(reply) == 0 { | |||||
return 0, ErrNil | |||||
} | |||||
n, err := strconv.ParseInt(reply, 10, 64) | |||||
return n, err | |||||
case nil: | |||||
return 0, ErrNil | |||||
case error: | |||||
return 0, reply | |||||
} | |||||
return 0, fmt.Errorf("redis cluster: unexpected type for Int64, got type %T", reply) | |||||
} | |||||
func Uint64(reply interface{}, err error) (uint64, error) { | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
switch reply := reply.(type) { | |||||
case uint: | |||||
return uint64(reply), nil | |||||
case uint8: | |||||
return uint64(reply), nil | |||||
case uint16: | |||||
return uint64(reply), nil | |||||
case uint32: | |||||
return uint64(reply), nil | |||||
case uint64: | |||||
return reply, nil | |||||
case int: | |||||
if reply < 0 { | |||||
return 0, ErrNegativeInt | |||||
} | |||||
return uint64(reply), nil | |||||
case int8: | |||||
if reply < 0 { | |||||
return 0, ErrNegativeInt | |||||
} | |||||
return uint64(reply), nil | |||||
case int16: | |||||
if reply < 0 { | |||||
return 0, ErrNegativeInt | |||||
} | |||||
return uint64(reply), nil | |||||
case int32: | |||||
if reply < 0 { | |||||
return 0, ErrNegativeInt | |||||
} | |||||
return uint64(reply), nil | |||||
case int64: | |||||
if reply < 0 { | |||||
return 0, ErrNegativeInt | |||||
} | |||||
return uint64(reply), nil | |||||
case []byte: | |||||
data := string(reply) | |||||
if len(data) == 0 { | |||||
return 0, ErrNil | |||||
} | |||||
n, err := strconv.ParseUint(data, 10, 64) | |||||
return n, err | |||||
case string: | |||||
if len(reply) == 0 { | |||||
return 0, ErrNil | |||||
} | |||||
n, err := strconv.ParseUint(reply, 10, 64) | |||||
return n, err | |||||
case nil: | |||||
return 0, ErrNil | |||||
case error: | |||||
return 0, reply | |||||
} | |||||
return 0, fmt.Errorf("redis cluster: unexpected type for Uint64, got type %T", reply) | |||||
} | |||||
func Float64(reply interface{}, err error) (float64, error) { | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
var value float64 | |||||
err = nil | |||||
switch v := reply.(type) { | |||||
case float32: | |||||
value = float64(v) | |||||
case float64: | |||||
value = v | |||||
case int: | |||||
value = float64(v) | |||||
case int8: | |||||
value = float64(v) | |||||
case int16: | |||||
value = float64(v) | |||||
case int32: | |||||
value = float64(v) | |||||
case int64: | |||||
value = float64(v) | |||||
case uint: | |||||
value = float64(v) | |||||
case uint8: | |||||
value = float64(v) | |||||
case uint16: | |||||
value = float64(v) | |||||
case uint32: | |||||
value = float64(v) | |||||
case uint64: | |||||
value = float64(v) | |||||
case []byte: | |||||
data := string(v) | |||||
if len(data) == 0 { | |||||
return 0, ErrNil | |||||
} | |||||
value, err = strconv.ParseFloat(string(v), 64) | |||||
case string: | |||||
if len(v) == 0 { | |||||
return 0, ErrNil | |||||
} | |||||
value, err = strconv.ParseFloat(v, 64) | |||||
case nil: | |||||
err = ErrNil | |||||
case error: | |||||
err = v | |||||
default: | |||||
err = fmt.Errorf("redis cluster: unexpected type for Float64, got type %T", v) | |||||
} | |||||
return value, err | |||||
} | |||||
func Bool(reply interface{}, err error) (bool, error) { | |||||
if err != nil { | |||||
return false, err | |||||
} | |||||
switch reply := reply.(type) { | |||||
case bool: | |||||
return reply, nil | |||||
case int64: | |||||
return reply != 0, nil | |||||
case []byte: | |||||
data := string(reply) | |||||
if len(data) == 0 { | |||||
return false, ErrNil | |||||
} | |||||
return strconv.ParseBool(data) | |||||
case string: | |||||
if len(reply) == 0 { | |||||
return false, ErrNil | |||||
} | |||||
return strconv.ParseBool(reply) | |||||
case nil: | |||||
return false, ErrNil | |||||
case error: | |||||
return false, reply | |||||
} | |||||
return false, fmt.Errorf("redis cluster: unexpected type for Bool, got type %T", reply) | |||||
} | |||||
func Bytes(reply interface{}, err error) ([]byte, error) { | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
switch reply := reply.(type) { | |||||
case []byte: | |||||
if len(reply) == 0 { | |||||
return nil, ErrNil | |||||
} | |||||
return reply, nil | |||||
case string: | |||||
data := []byte(reply) | |||||
if len(data) == 0 { | |||||
return nil, ErrNil | |||||
} | |||||
return data, nil | |||||
case nil: | |||||
return nil, ErrNil | |||||
case error: | |||||
return nil, reply | |||||
} | |||||
return nil, fmt.Errorf("redis cluster: unexpected type for Bytes, got type %T", reply) | |||||
} | |||||
func String(reply interface{}, err error) (string, error) { | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
value := "" | |||||
err = nil | |||||
switch v := reply.(type) { | |||||
case string: | |||||
if len(v) == 0 { | |||||
return "", ErrNil | |||||
} | |||||
value = v | |||||
case []byte: | |||||
if len(v) == 0 { | |||||
return "", ErrNil | |||||
} | |||||
value = string(v) | |||||
case int: | |||||
value = strconv.FormatInt(int64(v), 10) | |||||
case int8: | |||||
value = strconv.FormatInt(int64(v), 10) | |||||
case int16: | |||||
value = strconv.FormatInt(int64(v), 10) | |||||
case int32: | |||||
value = strconv.FormatInt(int64(v), 10) | |||||
case int64: | |||||
value = strconv.FormatInt(v, 10) | |||||
case uint: | |||||
value = strconv.FormatUint(uint64(v), 10) | |||||
case uint8: | |||||
value = strconv.FormatUint(uint64(v), 10) | |||||
case uint16: | |||||
value = strconv.FormatUint(uint64(v), 10) | |||||
case uint32: | |||||
value = strconv.FormatUint(uint64(v), 10) | |||||
case uint64: | |||||
value = strconv.FormatUint(v, 10) | |||||
case float32: | |||||
value = strconv.FormatFloat(float64(v), 'f', -1, 32) | |||||
case float64: | |||||
value = strconv.FormatFloat(v, 'f', -1, 64) | |||||
case bool: | |||||
value = strconv.FormatBool(v) | |||||
case nil: | |||||
err = ErrNil | |||||
case error: | |||||
err = v | |||||
default: | |||||
err = fmt.Errorf("redis cluster: unexpected type for String, got type %T", v) | |||||
} | |||||
return value, err | |||||
} | |||||
func Strings(reply interface{}, err error) ([]string, error) { | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
switch reply := reply.(type) { | |||||
case []interface{}: | |||||
result := make([]string, len(reply)) | |||||
for i := range reply { | |||||
if reply[i] == nil { | |||||
continue | |||||
} | |||||
switch subReply := reply[i].(type) { | |||||
case string: | |||||
result[i] = subReply | |||||
case []byte: | |||||
result[i] = string(subReply) | |||||
default: | |||||
return nil, fmt.Errorf("redis cluster: unexpected element type for String, got type %T", reply[i]) | |||||
} | |||||
} | |||||
return result, nil | |||||
case []string: | |||||
return reply, nil | |||||
case nil: | |||||
return nil, ErrNil | |||||
case error: | |||||
return nil, reply | |||||
} | |||||
return nil, fmt.Errorf("redis cluster: unexpected type for Strings, got type %T", reply) | |||||
} | |||||
func Values(reply interface{}, err error) ([]interface{}, error) { | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
switch reply := reply.(type) { | |||||
case []interface{}: | |||||
return reply, nil | |||||
case nil: | |||||
return nil, ErrNil | |||||
case error: | |||||
return nil, reply | |||||
} | |||||
return nil, fmt.Errorf("redis cluster: unexpected type for Values, got type %T", reply) | |||||
} |
@@ -0,0 +1,107 @@ | |||||
package cache | |||||
import ( | |||||
"fmt" | |||||
"time" | |||||
) | |||||
var c Cache | |||||
type Cache interface { | |||||
// get cached value by key. | |||||
Get(key string) interface{} | |||||
// GetMulti is a batch version of Get. | |||||
GetMulti(keys []string) []interface{} | |||||
// set cached value with key and expire time. | |||||
Put(key string, val interface{}, timeout time.Duration) error | |||||
// delete cached value by key. | |||||
Delete(key string) error | |||||
// increase cached int value by key, as a counter. | |||||
Incr(key string) error | |||||
// decrease cached int value by key, as a counter. | |||||
Decr(key string) error | |||||
// check if cached value exists or not. | |||||
IsExist(key string) bool | |||||
// clear all cache. | |||||
ClearAll() error | |||||
// start gc routine based on config string settings. | |||||
StartAndGC(config string) error | |||||
} | |||||
// Instance is a function create a new Cache Instance | |||||
type Instance func() Cache | |||||
var adapters = make(map[string]Instance) | |||||
// Register makes a cache adapter available by the adapter name. | |||||
// If Register is called twice with the same name or if driver is nil, | |||||
// it panics. | |||||
func Register(name string, adapter Instance) { | |||||
if adapter == nil { | |||||
panic("cache: Register adapter is nil") | |||||
} | |||||
if _, ok := adapters[name]; ok { | |||||
panic("cache: Register called twice for adapter " + name) | |||||
} | |||||
adapters[name] = adapter | |||||
} | |||||
// NewCache Create a new cache driver by adapter name and config string. | |||||
// config need to be correct JSON as string: {"interval":360}. | |||||
// it will start gc automatically. | |||||
func NewCache(adapterName, config string) (adapter Cache, err error) { | |||||
instanceFunc, ok := adapters[adapterName] | |||||
if !ok { | |||||
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) | |||||
return | |||||
} | |||||
adapter = instanceFunc() | |||||
err = adapter.StartAndGC(config) | |||||
if err != nil { | |||||
adapter = nil | |||||
} | |||||
return | |||||
} | |||||
func InitCache(adapterName, config string) (err error) { | |||||
instanceFunc, ok := adapters[adapterName] | |||||
if !ok { | |||||
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) | |||||
return | |||||
} | |||||
c = instanceFunc() | |||||
err = c.StartAndGC(config) | |||||
if err != nil { | |||||
c = nil | |||||
} | |||||
return | |||||
} | |||||
func Get(key string) interface{} { | |||||
return c.Get(key) | |||||
} | |||||
func GetMulti(keys []string) []interface{} { | |||||
return c.GetMulti(keys) | |||||
} | |||||
func Put(key string, val interface{}, ttl time.Duration) error { | |||||
return c.Put(key, val, ttl) | |||||
} | |||||
func Delete(key string) error { | |||||
return c.Delete(key) | |||||
} | |||||
func Incr(key string) error { | |||||
return c.Incr(key) | |||||
} | |||||
func Decr(key string) error { | |||||
return c.Decr(key) | |||||
} | |||||
func IsExist(key string) bool { | |||||
return c.IsExist(key) | |||||
} | |||||
func ClearAll() error { | |||||
return c.ClearAll() | |||||
} | |||||
func StartAndGC(cfg string) error { | |||||
return c.StartAndGC(cfg) | |||||
} |
@@ -0,0 +1,86 @@ | |||||
package cache | |||||
import ( | |||||
"fmt" | |||||
"strconv" | |||||
) | |||||
// GetString convert interface to string. | |||||
func GetString(v interface{}) string { | |||||
switch result := v.(type) { | |||||
case string: | |||||
return result | |||||
case []byte: | |||||
return string(result) | |||||
default: | |||||
if v != nil { | |||||
return fmt.Sprint(result) | |||||
} | |||||
} | |||||
return "" | |||||
} | |||||
// GetInt convert interface to int. | |||||
func GetInt(v interface{}) int { | |||||
switch result := v.(type) { | |||||
case int: | |||||
return result | |||||
case int32: | |||||
return int(result) | |||||
case int64: | |||||
return int(result) | |||||
default: | |||||
if d := GetString(v); d != "" { | |||||
value, _ := strconv.Atoi(d) | |||||
return value | |||||
} | |||||
} | |||||
return 0 | |||||
} | |||||
// GetInt64 convert interface to int64. | |||||
func GetInt64(v interface{}) int64 { | |||||
switch result := v.(type) { | |||||
case int: | |||||
return int64(result) | |||||
case int32: | |||||
return int64(result) | |||||
case int64: | |||||
return result | |||||
default: | |||||
if d := GetString(v); d != "" { | |||||
value, _ := strconv.ParseInt(d, 10, 64) | |||||
return value | |||||
} | |||||
} | |||||
return 0 | |||||
} | |||||
// GetFloat64 convert interface to float64. | |||||
func GetFloat64(v interface{}) float64 { | |||||
switch result := v.(type) { | |||||
case float64: | |||||
return result | |||||
default: | |||||
if d := GetString(v); d != "" { | |||||
value, _ := strconv.ParseFloat(d, 64) | |||||
return value | |||||
} | |||||
} | |||||
return 0 | |||||
} | |||||
// GetBool convert interface to bool. | |||||
func GetBool(v interface{}) bool { | |||||
switch result := v.(type) { | |||||
case bool: | |||||
return result | |||||
default: | |||||
if d := GetString(v); d != "" { | |||||
value, _ := strconv.ParseBool(d) | |||||
return value | |||||
} | |||||
} | |||||
return false | |||||
} |
@@ -0,0 +1,241 @@ | |||||
package cache | |||||
import ( | |||||
"bytes" | |||||
"crypto/md5" | |||||
"encoding/gob" | |||||
"encoding/hex" | |||||
"encoding/json" | |||||
"fmt" | |||||
"io" | |||||
"io/ioutil" | |||||
"os" | |||||
"path/filepath" | |||||
"reflect" | |||||
"strconv" | |||||
"time" | |||||
) | |||||
// FileCacheItem is basic unit of file cache adapter. | |||||
// it contains data and expire time. | |||||
type FileCacheItem struct { | |||||
Data interface{} | |||||
LastAccess time.Time | |||||
Expired time.Time | |||||
} | |||||
// FileCache Config | |||||
var ( | |||||
FileCachePath = "cache" // cache directory | |||||
FileCacheFileSuffix = ".bin" // cache file suffix | |||||
FileCacheDirectoryLevel = 2 // cache file deep level if auto generated cache files. | |||||
FileCacheEmbedExpiry time.Duration // cache expire time, default is no expire forever. | |||||
) | |||||
// FileCache is cache adapter for file storage. | |||||
type FileCache struct { | |||||
CachePath string | |||||
FileSuffix string | |||||
DirectoryLevel int | |||||
EmbedExpiry int | |||||
} | |||||
// NewFileCache Create new file cache with no config. | |||||
// the level and expiry need set in method StartAndGC as config string. | |||||
func NewFileCache() Cache { | |||||
// return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix} | |||||
return &FileCache{} | |||||
} | |||||
// StartAndGC will start and begin gc for file cache. | |||||
// the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0} | |||||
func (fc *FileCache) StartAndGC(config string) error { | |||||
var cfg map[string]string | |||||
json.Unmarshal([]byte(config), &cfg) | |||||
if _, ok := cfg["CachePath"]; !ok { | |||||
cfg["CachePath"] = FileCachePath | |||||
} | |||||
if _, ok := cfg["FileSuffix"]; !ok { | |||||
cfg["FileSuffix"] = FileCacheFileSuffix | |||||
} | |||||
if _, ok := cfg["DirectoryLevel"]; !ok { | |||||
cfg["DirectoryLevel"] = strconv.Itoa(FileCacheDirectoryLevel) | |||||
} | |||||
if _, ok := cfg["EmbedExpiry"]; !ok { | |||||
cfg["EmbedExpiry"] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10) | |||||
} | |||||
fc.CachePath = cfg["CachePath"] | |||||
fc.FileSuffix = cfg["FileSuffix"] | |||||
fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"]) | |||||
fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"]) | |||||
fc.Init() | |||||
return nil | |||||
} | |||||
// Init will make new dir for file cache if not exist. | |||||
func (fc *FileCache) Init() { | |||||
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle | |||||
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle | |||||
} | |||||
} | |||||
// get cached file name. it's md5 encoded. | |||||
func (fc *FileCache) getCacheFileName(key string) string { | |||||
m := md5.New() | |||||
io.WriteString(m, key) | |||||
keyMd5 := hex.EncodeToString(m.Sum(nil)) | |||||
cachePath := fc.CachePath | |||||
switch fc.DirectoryLevel { | |||||
case 2: | |||||
cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4]) | |||||
case 1: | |||||
cachePath = filepath.Join(cachePath, keyMd5[0:2]) | |||||
} | |||||
if ok, _ := exists(cachePath); !ok { // todo : error handle | |||||
_ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle | |||||
} | |||||
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix)) | |||||
} | |||||
// Get value from file cache. | |||||
// if non-exist or expired, return empty string. | |||||
func (fc *FileCache) Get(key string) interface{} { | |||||
fileData, err := FileGetContents(fc.getCacheFileName(key)) | |||||
if err != nil { | |||||
return "" | |||||
} | |||||
var to FileCacheItem | |||||
GobDecode(fileData, &to) | |||||
if to.Expired.Before(time.Now()) { | |||||
return "" | |||||
} | |||||
return to.Data | |||||
} | |||||
// GetMulti gets values from file cache. | |||||
// if non-exist or expired, return empty string. | |||||
func (fc *FileCache) GetMulti(keys []string) []interface{} { | |||||
var rc []interface{} | |||||
for _, key := range keys { | |||||
rc = append(rc, fc.Get(key)) | |||||
} | |||||
return rc | |||||
} | |||||
// Put value into file cache. | |||||
// timeout means how long to keep this file, unit of ms. | |||||
// if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever. | |||||
func (fc *FileCache) Put(key string, val interface{}, timeout time.Duration) error { | |||||
gob.Register(val) | |||||
item := FileCacheItem{Data: val} | |||||
if timeout == FileCacheEmbedExpiry { | |||||
item.Expired = time.Now().Add((86400 * 365 * 10) * time.Second) // ten years | |||||
} else { | |||||
item.Expired = time.Now().Add(timeout) | |||||
} | |||||
item.LastAccess = time.Now() | |||||
data, err := GobEncode(item) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return FilePutContents(fc.getCacheFileName(key), data) | |||||
} | |||||
// Delete file cache value. | |||||
func (fc *FileCache) Delete(key string) error { | |||||
filename := fc.getCacheFileName(key) | |||||
if ok, _ := exists(filename); ok { | |||||
return os.Remove(filename) | |||||
} | |||||
return nil | |||||
} | |||||
// Incr will increase cached int value. | |||||
// fc value is saving forever unless Delete. | |||||
func (fc *FileCache) Incr(key string) error { | |||||
data := fc.Get(key) | |||||
var incr int | |||||
if reflect.TypeOf(data).Name() != "int" { | |||||
incr = 0 | |||||
} else { | |||||
incr = data.(int) + 1 | |||||
} | |||||
fc.Put(key, incr, FileCacheEmbedExpiry) | |||||
return nil | |||||
} | |||||
// Decr will decrease cached int value. | |||||
func (fc *FileCache) Decr(key string) error { | |||||
data := fc.Get(key) | |||||
var decr int | |||||
if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 { | |||||
decr = 0 | |||||
} else { | |||||
decr = data.(int) - 1 | |||||
} | |||||
fc.Put(key, decr, FileCacheEmbedExpiry) | |||||
return nil | |||||
} | |||||
// IsExist check value is exist. | |||||
func (fc *FileCache) IsExist(key string) bool { | |||||
ret, _ := exists(fc.getCacheFileName(key)) | |||||
return ret | |||||
} | |||||
// ClearAll will clean cached files. | |||||
// not implemented. | |||||
func (fc *FileCache) ClearAll() error { | |||||
return nil | |||||
} | |||||
// check file exist. | |||||
func exists(path string) (bool, error) { | |||||
_, err := os.Stat(path) | |||||
if err == nil { | |||||
return true, nil | |||||
} | |||||
if os.IsNotExist(err) { | |||||
return false, nil | |||||
} | |||||
return false, err | |||||
} | |||||
// FileGetContents Get bytes to file. | |||||
// if non-exist, create this file. | |||||
func FileGetContents(filename string) (data []byte, e error) { | |||||
return ioutil.ReadFile(filename) | |||||
} | |||||
// FilePutContents Put bytes to file. | |||||
// if non-exist, create this file. | |||||
func FilePutContents(filename string, content []byte) error { | |||||
return ioutil.WriteFile(filename, content, os.ModePerm) | |||||
} | |||||
// GobEncode Gob encodes file cache item. | |||||
func GobEncode(data interface{}) ([]byte, error) { | |||||
buf := bytes.NewBuffer(nil) | |||||
enc := gob.NewEncoder(buf) | |||||
err := enc.Encode(data) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return buf.Bytes(), err | |||||
} | |||||
// GobDecode Gob decodes file cache item. | |||||
func GobDecode(data []byte, to *FileCacheItem) error { | |||||
buf := bytes.NewBuffer(data) | |||||
dec := gob.NewDecoder(buf) | |||||
return dec.Decode(&to) | |||||
} | |||||
func init() { | |||||
Register("file", NewFileCache) | |||||
} |
@@ -0,0 +1,239 @@ | |||||
package cache | |||||
import ( | |||||
"encoding/json" | |||||
"errors" | |||||
"sync" | |||||
"time" | |||||
) | |||||
var ( | |||||
// DefaultEvery means the clock time of recycling the expired cache items in memory. | |||||
DefaultEvery = 60 // 1 minute | |||||
) | |||||
// MemoryItem store memory cache item. | |||||
type MemoryItem struct { | |||||
val interface{} | |||||
createdTime time.Time | |||||
lifespan time.Duration | |||||
} | |||||
func (mi *MemoryItem) isExpire() bool { | |||||
// 0 means forever | |||||
if mi.lifespan == 0 { | |||||
return false | |||||
} | |||||
return time.Now().Sub(mi.createdTime) > mi.lifespan | |||||
} | |||||
// MemoryCache is Memory cache adapter. | |||||
// it contains a RW locker for safe map storage. | |||||
type MemoryCache struct { | |||||
sync.RWMutex | |||||
dur time.Duration | |||||
items map[string]*MemoryItem | |||||
Every int // run an expiration check Every clock time | |||||
} | |||||
// NewMemoryCache returns a new MemoryCache. | |||||
func NewMemoryCache() Cache { | |||||
cache := MemoryCache{items: make(map[string]*MemoryItem)} | |||||
return &cache | |||||
} | |||||
// Get cache from memory. | |||||
// if non-existed or expired, return nil. | |||||
func (bc *MemoryCache) Get(name string) interface{} { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
if itm, ok := bc.items[name]; ok { | |||||
if itm.isExpire() { | |||||
return nil | |||||
} | |||||
return itm.val | |||||
} | |||||
return nil | |||||
} | |||||
// GetMulti gets caches from memory. | |||||
// if non-existed or expired, return nil. | |||||
func (bc *MemoryCache) GetMulti(names []string) []interface{} { | |||||
var rc []interface{} | |||||
for _, name := range names { | |||||
rc = append(rc, bc.Get(name)) | |||||
} | |||||
return rc | |||||
} | |||||
// Put cache to memory. | |||||
// if lifespan is 0, it will be forever till restart. | |||||
func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error { | |||||
bc.Lock() | |||||
defer bc.Unlock() | |||||
bc.items[name] = &MemoryItem{ | |||||
val: value, | |||||
createdTime: time.Now(), | |||||
lifespan: lifespan, | |||||
} | |||||
return nil | |||||
} | |||||
// Delete cache in memory. | |||||
func (bc *MemoryCache) Delete(name string) error { | |||||
bc.Lock() | |||||
defer bc.Unlock() | |||||
if _, ok := bc.items[name]; !ok { | |||||
return errors.New("key not exist") | |||||
} | |||||
delete(bc.items, name) | |||||
if _, ok := bc.items[name]; ok { | |||||
return errors.New("delete key error") | |||||
} | |||||
return nil | |||||
} | |||||
// Incr increase cache counter in memory. | |||||
// it supports int,int32,int64,uint,uint32,uint64. | |||||
func (bc *MemoryCache) Incr(key string) error { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
itm, ok := bc.items[key] | |||||
if !ok { | |||||
return errors.New("key not exist") | |||||
} | |||||
switch itm.val.(type) { | |||||
case int: | |||||
itm.val = itm.val.(int) + 1 | |||||
case int32: | |||||
itm.val = itm.val.(int32) + 1 | |||||
case int64: | |||||
itm.val = itm.val.(int64) + 1 | |||||
case uint: | |||||
itm.val = itm.val.(uint) + 1 | |||||
case uint32: | |||||
itm.val = itm.val.(uint32) + 1 | |||||
case uint64: | |||||
itm.val = itm.val.(uint64) + 1 | |||||
default: | |||||
return errors.New("item val is not (u)int (u)int32 (u)int64") | |||||
} | |||||
return nil | |||||
} | |||||
// Decr decrease counter in memory. | |||||
func (bc *MemoryCache) Decr(key string) error { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
itm, ok := bc.items[key] | |||||
if !ok { | |||||
return errors.New("key not exist") | |||||
} | |||||
switch itm.val.(type) { | |||||
case int: | |||||
itm.val = itm.val.(int) - 1 | |||||
case int64: | |||||
itm.val = itm.val.(int64) - 1 | |||||
case int32: | |||||
itm.val = itm.val.(int32) - 1 | |||||
case uint: | |||||
if itm.val.(uint) > 0 { | |||||
itm.val = itm.val.(uint) - 1 | |||||
} else { | |||||
return errors.New("item val is less than 0") | |||||
} | |||||
case uint32: | |||||
if itm.val.(uint32) > 0 { | |||||
itm.val = itm.val.(uint32) - 1 | |||||
} else { | |||||
return errors.New("item val is less than 0") | |||||
} | |||||
case uint64: | |||||
if itm.val.(uint64) > 0 { | |||||
itm.val = itm.val.(uint64) - 1 | |||||
} else { | |||||
return errors.New("item val is less than 0") | |||||
} | |||||
default: | |||||
return errors.New("item val is not int int64 int32") | |||||
} | |||||
return nil | |||||
} | |||||
// IsExist check cache exist in memory. | |||||
func (bc *MemoryCache) IsExist(name string) bool { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
if v, ok := bc.items[name]; ok { | |||||
return !v.isExpire() | |||||
} | |||||
return false | |||||
} | |||||
// ClearAll will delete all cache in memory. | |||||
func (bc *MemoryCache) ClearAll() error { | |||||
bc.Lock() | |||||
defer bc.Unlock() | |||||
bc.items = make(map[string]*MemoryItem) | |||||
return nil | |||||
} | |||||
// StartAndGC start memory cache. it will check expiration in every clock time. | |||||
func (bc *MemoryCache) StartAndGC(config string) error { | |||||
var cf map[string]int | |||||
json.Unmarshal([]byte(config), &cf) | |||||
if _, ok := cf["interval"]; !ok { | |||||
cf = make(map[string]int) | |||||
cf["interval"] = DefaultEvery | |||||
} | |||||
dur := time.Duration(cf["interval"]) * time.Second | |||||
bc.Every = cf["interval"] | |||||
bc.dur = dur | |||||
go bc.vacuum() | |||||
return nil | |||||
} | |||||
// check expiration. | |||||
func (bc *MemoryCache) vacuum() { | |||||
bc.RLock() | |||||
every := bc.Every | |||||
bc.RUnlock() | |||||
if every < 1 { | |||||
return | |||||
} | |||||
for { | |||||
<-time.After(bc.dur) | |||||
if bc.items == nil { | |||||
return | |||||
} | |||||
if keys := bc.expiredKeys(); len(keys) != 0 { | |||||
bc.clearItems(keys) | |||||
} | |||||
} | |||||
} | |||||
// expiredKeys returns key list which are expired. | |||||
func (bc *MemoryCache) expiredKeys() (keys []string) { | |||||
bc.RLock() | |||||
defer bc.RUnlock() | |||||
for key, itm := range bc.items { | |||||
if itm.isExpire() { | |||||
keys = append(keys, key) | |||||
} | |||||
} | |||||
return | |||||
} | |||||
// clearItems removes all the items which key in keys. | |||||
func (bc *MemoryCache) clearItems(keys []string) { | |||||
bc.Lock() | |||||
defer bc.Unlock() | |||||
for _, key := range keys { | |||||
delete(bc.items, key) | |||||
} | |||||
} | |||||
func init() { | |||||
Register("memory", NewMemoryCache) | |||||
} |
@@ -0,0 +1,412 @@ | |||||
package cache | |||||
import ( | |||||
"encoding/json" | |||||
"errors" | |||||
"log" | |||||
"strings" | |||||
"time" | |||||
redigo "github.com/gomodule/redigo/redis" | |||||
) | |||||
// configuration | |||||
type Config struct { | |||||
Server string | |||||
Password string | |||||
MaxIdle int // Maximum number of idle connections in the pool. | |||||
// Maximum number of connections allocated by the pool at a given time. | |||||
// When zero, there is no limit on the number of connections in the pool. | |||||
MaxActive int | |||||
// Close connections after remaining idle for this duration. If the value | |||||
// is zero, then idle connections are not closed. Applications should set | |||||
// the timeout to a value less than the server's timeout. | |||||
IdleTimeout time.Duration | |||||
// If Wait is true and the pool is at the MaxActive limit, then Get() waits | |||||
// for a connection to be returned to the pool before returning. | |||||
Wait bool | |||||
KeyPrefix string // prefix to all keys; example is "dev environment name" | |||||
KeyDelimiter string // delimiter to be used while appending keys; example is ":" | |||||
KeyPlaceholder string // placeholder to be parsed using given arguments to obtain a final key; example is "?" | |||||
} | |||||
var pool *redigo.Pool | |||||
var conf *Config | |||||
func NewRedis(addr string) { | |||||
if addr == "" { | |||||
panic("\nredis connect string cannot be empty\n") | |||||
} | |||||
pool = &redigo.Pool{ | |||||
MaxIdle: redisMaxIdleConn, | |||||
IdleTimeout: redisIdleTTL, | |||||
MaxActive: redisMaxActive, | |||||
// MaxConnLifetime: redisDialTTL, | |||||
Wait: true, | |||||
Dial: func() (redigo.Conn, error) { | |||||
c, err := redigo.Dial("tcp", addr, | |||||
redigo.DialConnectTimeout(redisDialTTL), | |||||
redigo.DialReadTimeout(redisReadTTL), | |||||
redigo.DialWriteTimeout(redisWriteTTL), | |||||
) | |||||
if err != nil { | |||||
log.Println("Redis Dial failed: ", err) | |||||
return nil, err | |||||
} | |||||
return c, err | |||||
}, | |||||
TestOnBorrow: func(c redigo.Conn, t time.Time) error { | |||||
_, err := c.Do("PING") | |||||
if err != nil { | |||||
log.Println("Unable to ping to redis server:", err) | |||||
} | |||||
return err | |||||
}, | |||||
} | |||||
conn := pool.Get() | |||||
defer conn.Close() | |||||
if conn.Err() != nil { | |||||
println("\nredis connect " + addr + " error: " + conn.Err().Error()) | |||||
} else { | |||||
println("\nredis connect " + addr + " success!\n") | |||||
} | |||||
} | |||||
func Do(cmd string, args ...interface{}) (reply interface{}, err error) { | |||||
conn := pool.Get() | |||||
defer conn.Close() | |||||
return conn.Do(cmd, args...) | |||||
} | |||||
func GetPool() *redigo.Pool { | |||||
return pool | |||||
} | |||||
func ParseKey(key string, vars []string) (string, error) { | |||||
arr := strings.Split(key, conf.KeyPlaceholder) | |||||
actualKey := "" | |||||
if len(arr) != len(vars)+1 { | |||||
return "", errors.New("redis/connection.go: Insufficient arguments to parse key") | |||||
} else { | |||||
for index, val := range arr { | |||||
if index == 0 { | |||||
actualKey = arr[index] | |||||
} else { | |||||
actualKey += vars[index-1] + val | |||||
} | |||||
} | |||||
} | |||||
return getPrefixedKey(actualKey), nil | |||||
} | |||||
func getPrefixedKey(key string) string { | |||||
return conf.KeyPrefix + conf.KeyDelimiter + key | |||||
} | |||||
func StripEnvKey(key string) string { | |||||
return strings.TrimLeft(key, conf.KeyPrefix+conf.KeyDelimiter) | |||||
} | |||||
func SplitKey(key string) []string { | |||||
return strings.Split(key, conf.KeyDelimiter) | |||||
} | |||||
func Expire(key string, ttl int) (interface{}, error) { | |||||
return Do("EXPIRE", key, ttl) | |||||
} | |||||
func Persist(key string) (interface{}, error) { | |||||
return Do("PERSIST", key) | |||||
} | |||||
func Del(key string) (interface{}, error) { | |||||
return Do("DEL", key) | |||||
} | |||||
func Set(key string, data interface{}) (interface{}, error) { | |||||
// set | |||||
return Do("SET", key, data) | |||||
} | |||||
func SetNX(key string, data interface{}) (interface{}, error) { | |||||
return Do("SETNX", key, data) | |||||
} | |||||
func SetEx(key string, data interface{}, ttl int) (interface{}, error) { | |||||
return Do("SETEX", key, ttl, data) | |||||
} | |||||
func SetJson(key string, data interface{}, ttl int) bool { | |||||
c, err := json.Marshal(data) | |||||
if err != nil { | |||||
return false | |||||
} | |||||
if ttl < 1 { | |||||
_, err = Set(key, c) | |||||
} else { | |||||
_, err = SetEx(key, c, ttl) | |||||
} | |||||
if err != nil { | |||||
return false | |||||
} | |||||
return true | |||||
} | |||||
func GetJson(key string, dst interface{}) error { | |||||
b, err := GetBytes(key) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if err = json.Unmarshal(b, dst); err != nil { | |||||
return err | |||||
} | |||||
return nil | |||||
} | |||||
func Get(key string) (interface{}, error) { | |||||
// get | |||||
return Do("GET", key) | |||||
} | |||||
func GetTTL(key string) (time.Duration, error) { | |||||
ttl, err := redigo.Int64(Do("TTL", key)) | |||||
return time.Duration(ttl) * time.Second, err | |||||
} | |||||
func GetBytes(key string) ([]byte, error) { | |||||
return redigo.Bytes(Do("GET", key)) | |||||
} | |||||
func GetString(key string) (string, error) { | |||||
return redigo.String(Do("GET", key)) | |||||
} | |||||
func GetStringMap(key string) (map[string]string, error) { | |||||
return redigo.StringMap(Do("GET", key)) | |||||
} | |||||
func GetInt(key string) (int, error) { | |||||
return redigo.Int(Do("GET", key)) | |||||
} | |||||
func GetInt64(key string) (int64, error) { | |||||
return redigo.Int64(Do("GET", key)) | |||||
} | |||||
func GetStringLength(key string) (int, error) { | |||||
return redigo.Int(Do("STRLEN", key)) | |||||
} | |||||
func ZAdd(key string, score float64, data interface{}) (interface{}, error) { | |||||
return Do("ZADD", key, score, data) | |||||
} | |||||
func ZAddNX(key string, score float64, data interface{}) (interface{}, error) { | |||||
return Do("ZADD", key, "NX", score, data) | |||||
} | |||||
func ZRem(key string, data interface{}) (interface{}, error) { | |||||
return Do("ZREM", key, data) | |||||
} | |||||
func ZRange(key string, start int, end int, withScores bool) ([]interface{}, error) { | |||||
if withScores { | |||||
return redigo.Values(Do("ZRANGE", key, start, end, "WITHSCORES")) | |||||
} | |||||
return redigo.Values(Do("ZRANGE", key, start, end)) | |||||
} | |||||
func ZRemRangeByScore(key string, start int64, end int64) ([]interface{}, error) { | |||||
return redigo.Values(Do("ZREMRANGEBYSCORE", key, start, end)) | |||||
} | |||||
func ZCard(setName string) (int64, error) { | |||||
return redigo.Int64(Do("ZCARD", setName)) | |||||
} | |||||
func ZScan(setName string) (int64, error) { | |||||
return redigo.Int64(Do("ZCARD", setName)) | |||||
} | |||||
func SAdd(setName string, data interface{}) (interface{}, error) { | |||||
return Do("SADD", setName, data) | |||||
} | |||||
func SCard(setName string) (int64, error) { | |||||
return redigo.Int64(Do("SCARD", setName)) | |||||
} | |||||
func SIsMember(setName string, data interface{}) (bool, error) { | |||||
return redigo.Bool(Do("SISMEMBER", setName, data)) | |||||
} | |||||
func SMembers(setName string) ([]string, error) { | |||||
return redigo.Strings(Do("SMEMBERS", setName)) | |||||
} | |||||
func SRem(setName string, data interface{}) (interface{}, error) { | |||||
return Do("SREM", setName, data) | |||||
} | |||||
func HSet(key string, HKey string, data interface{}) (interface{}, error) { | |||||
return Do("HSET", key, HKey, data) | |||||
} | |||||
func HGet(key string, HKey string) (interface{}, error) { | |||||
return Do("HGET", key, HKey) | |||||
} | |||||
func HMGet(key string, hashKeys ...string) ([]interface{}, error) { | |||||
ret, err := Do("HMGET", key, hashKeys) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
reta, ok := ret.([]interface{}) | |||||
if !ok { | |||||
return nil, errors.New("result not an array") | |||||
} | |||||
return reta, nil | |||||
} | |||||
func HMSet(key string, hashKeys []string, vals []interface{}) (interface{}, error) { | |||||
if len(hashKeys) == 0 || len(hashKeys) != len(vals) { | |||||
var ret interface{} | |||||
return ret, errors.New("bad length") | |||||
} | |||||
input := []interface{}{key} | |||||
for i, v := range hashKeys { | |||||
input = append(input, v, vals[i]) | |||||
} | |||||
return Do("HMSET", input...) | |||||
} | |||||
func HGetString(key string, HKey string) (string, error) { | |||||
return redigo.String(Do("HGET", key, HKey)) | |||||
} | |||||
func HGetFloat(key string, HKey string) (float64, error) { | |||||
f, err := redigo.Float64(Do("HGET", key, HKey)) | |||||
return f, err | |||||
} | |||||
func HGetInt(key string, HKey string) (int, error) { | |||||
return redigo.Int(Do("HGET", key, HKey)) | |||||
} | |||||
func HGetInt64(key string, HKey string) (int64, error) { | |||||
return redigo.Int64(Do("HGET", key, HKey)) | |||||
} | |||||
func HGetBool(key string, HKey string) (bool, error) { | |||||
return redigo.Bool(Do("HGET", key, HKey)) | |||||
} | |||||
func HDel(key string, HKey string) (interface{}, error) { | |||||
return Do("HDEL", key, HKey) | |||||
} | |||||
func HGetAll(key string) (map[string]interface{}, error) { | |||||
vals, err := redigo.Values(Do("HGETALL", key)) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
num := len(vals) / 2 | |||||
result := make(map[string]interface{}, num) | |||||
for i := 0; i < num; i++ { | |||||
key, _ := redigo.String(vals[2*i], nil) | |||||
result[key] = vals[2*i+1] | |||||
} | |||||
return result, nil | |||||
} | |||||
func FlushAll() bool { | |||||
res, _ := redigo.String(Do("FLUSHALL")) | |||||
if res == "" { | |||||
return false | |||||
} | |||||
return true | |||||
} | |||||
// NOTE: Use this in production environment with extreme care. | |||||
// Read more here:https://redigo.io/commands/keys | |||||
func Keys(pattern string) ([]string, error) { | |||||
return redigo.Strings(Do("KEYS", pattern)) | |||||
} | |||||
func HKeys(key string) ([]string, error) { | |||||
return redigo.Strings(Do("HKEYS", key)) | |||||
} | |||||
func Exists(key string) bool { | |||||
count, err := redigo.Int(Do("EXISTS", key)) | |||||
if count == 0 || err != nil { | |||||
return false | |||||
} | |||||
return true | |||||
} | |||||
func Incr(key string) (int64, error) { | |||||
return redigo.Int64(Do("INCR", key)) | |||||
} | |||||
func Decr(key string) (int64, error) { | |||||
return redigo.Int64(Do("DECR", key)) | |||||
} | |||||
func IncrBy(key string, incBy int64) (int64, error) { | |||||
return redigo.Int64(Do("INCRBY", key, incBy)) | |||||
} | |||||
func DecrBy(key string, decrBy int64) (int64, error) { | |||||
return redigo.Int64(Do("DECRBY", key)) | |||||
} | |||||
func IncrByFloat(key string, incBy float64) (float64, error) { | |||||
return redigo.Float64(Do("INCRBYFLOAT", key, incBy)) | |||||
} | |||||
func DecrByFloat(key string, decrBy float64) (float64, error) { | |||||
return redigo.Float64(Do("DECRBYFLOAT", key, decrBy)) | |||||
} | |||||
// use for message queue | |||||
func LPush(key string, data interface{}) (interface{}, error) { | |||||
// set | |||||
return Do("LPUSH", key, data) | |||||
} | |||||
func LPop(key string) (interface{}, error) { | |||||
return Do("LPOP", key) | |||||
} | |||||
func LPopString(key string) (string, error) { | |||||
return redigo.String(Do("LPOP", key)) | |||||
} | |||||
func LPopFloat(key string) (float64, error) { | |||||
f, err := redigo.Float64(Do("LPOP", key)) | |||||
return f, err | |||||
} | |||||
func LPopInt(key string) (int, error) { | |||||
return redigo.Int(Do("LPOP", key)) | |||||
} | |||||
func LPopInt64(key string) (int64, error) { | |||||
return redigo.Int64(Do("LPOP", key)) | |||||
} | |||||
func RPush(key string, data interface{}) (interface{}, error) { | |||||
// set | |||||
return Do("RPUSH", key, data) | |||||
} | |||||
func RPop(key string) (interface{}, error) { | |||||
return Do("RPOP", key) | |||||
} | |||||
func RPopString(key string) (string, error) { | |||||
return redigo.String(Do("RPOP", key)) | |||||
} | |||||
func RPopFloat(key string) (float64, error) { | |||||
f, err := redigo.Float64(Do("RPOP", key)) | |||||
return f, err | |||||
} | |||||
func RPopInt(key string) (int, error) { | |||||
return redigo.Int(Do("RPOP", key)) | |||||
} | |||||
func RPopInt64(key string) (int64, error) { | |||||
return redigo.Int64(Do("RPOP", key)) | |||||
} | |||||
func Scan(cursor int64, pattern string, count int64) (int64, []string, error) { | |||||
var items []string | |||||
var newCursor int64 | |||||
values, err := redigo.Values(Do("SCAN", cursor, "MATCH", pattern, "COUNT", count)) | |||||
if err != nil { | |||||
return 0, nil, err | |||||
} | |||||
values, err = redigo.Scan(values, &newCursor, &items) | |||||
if err != nil { | |||||
return 0, nil, err | |||||
} | |||||
return newCursor, items, nil | |||||
} | |||||
func LPushMax(key string, data ...interface{}) (interface{}, error) { | |||||
// set | |||||
return Do("LPUSH", key, data) | |||||
} | |||||
func SelectDb(db int) (interface{}, error) { | |||||
return Do("SELECT", db) | |||||
} |