mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
remove license code
This commit is contained in:
parent
27c816cf3b
commit
4a99be2f15
@ -11,24 +11,21 @@ import (
|
|||||||
"geekai/core"
|
"geekai/core"
|
||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"geekai/handler"
|
"geekai/handler"
|
||||||
"geekai/service"
|
|
||||||
"geekai/store"
|
"geekai/store"
|
||||||
"geekai/store/model"
|
"geekai/store/model"
|
||||||
"geekai/utils"
|
"geekai/utils"
|
||||||
"geekai/utils/resp"
|
"geekai/utils/resp"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/shirou/gopsutil/host"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigHandler struct {
|
type ConfigHandler struct {
|
||||||
handler.BaseHandler
|
handler.BaseHandler
|
||||||
levelDB *store.LevelDB
|
levelDB *store.LevelDB
|
||||||
licenseService *service.LicenseService
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfigHandler(app *core.AppServer, db *gorm.DB, levelDB *store.LevelDB, licenseService *service.LicenseService) *ConfigHandler {
|
func NewConfigHandler(app *core.AppServer, db *gorm.DB, levelDB *store.LevelDB) *ConfigHandler {
|
||||||
return &ConfigHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, levelDB: levelDB, licenseService: licenseService}
|
return &ConfigHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, levelDB: levelDB}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ConfigHandler) Update(c *gin.Context) {
|
func (h *ConfigHandler) Update(c *gin.Context) {
|
||||||
@ -98,33 +95,3 @@ func (h *ConfigHandler) Get(c *gin.Context) {
|
|||||||
|
|
||||||
resp.SUCCESS(c, value)
|
resp.SUCCESS(c, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Active 激活系统
|
|
||||||
func (h *ConfigHandler) Active(c *gin.Context) {
|
|
||||||
var data struct {
|
|
||||||
License string `json:"license"`
|
|
||||||
}
|
|
||||||
if err := c.ShouldBindJSON(&data); err != nil {
|
|
||||||
resp.ERROR(c, types.InvalidArgs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
info, err := host.Info()
|
|
||||||
if err != nil {
|
|
||||||
resp.ERROR(c, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.licenseService.ActiveLicense(data.License, info.HostID)
|
|
||||||
if err != nil {
|
|
||||||
resp.ERROR(c, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.SUCCESS(c, info.HostID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLicense 获取 License 信息
|
|
||||||
func (h *ConfigHandler) GetLicense(c *gin.Context) {
|
|
||||||
license := h.licenseService.GetLicense()
|
|
||||||
resp.SUCCESS(c, license)
|
|
||||||
}
|
|
||||||
|
@ -8,15 +8,14 @@ package admin
|
|||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"geekai/core"
|
"geekai/core"
|
||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"geekai/handler"
|
"geekai/handler"
|
||||||
"geekai/service"
|
|
||||||
"geekai/store/model"
|
"geekai/store/model"
|
||||||
"geekai/store/vo"
|
"geekai/store/vo"
|
||||||
"geekai/utils"
|
"geekai/utils"
|
||||||
"geekai/utils/resp"
|
"geekai/utils/resp"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -25,11 +24,10 @@ import (
|
|||||||
|
|
||||||
type UserHandler struct {
|
type UserHandler struct {
|
||||||
handler.BaseHandler
|
handler.BaseHandler
|
||||||
licenseService *service.LicenseService
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserHandler(app *core.AppServer, db *gorm.DB, licenseService *service.LicenseService) *UserHandler {
|
func NewUserHandler(app *core.AppServer, db *gorm.DB) *UserHandler {
|
||||||
return &UserHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, licenseService: licenseService}
|
return &UserHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// List 用户列表
|
// List 用户列表
|
||||||
@ -84,13 +82,7 @@ func (h *UserHandler) Save(c *gin.Context) {
|
|||||||
resp.ERROR(c, types.InvalidArgs)
|
resp.ERROR(c, types.InvalidArgs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 检测最大注册人数
|
|
||||||
var totalUser int64
|
|
||||||
h.DB.Model(&model.User{}).Count(&totalUser)
|
|
||||||
if int(totalUser) >= h.licenseService.GetLicense().UserNum {
|
|
||||||
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var user = model.User{}
|
var user = model.User{}
|
||||||
var res *gorm.DB
|
var res *gorm.DB
|
||||||
var userVo vo.User
|
var userVo vo.User
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"geekai/handler"
|
"geekai/handler"
|
||||||
logger2 "geekai/logger"
|
logger2 "geekai/logger"
|
||||||
"geekai/service"
|
|
||||||
"geekai/service/oss"
|
"geekai/service/oss"
|
||||||
"geekai/store/model"
|
"geekai/store/model"
|
||||||
"geekai/store/vo"
|
"geekai/store/vo"
|
||||||
@ -43,17 +42,15 @@ var logger = logger2.GetLogger()
|
|||||||
|
|
||||||
type ChatHandler struct {
|
type ChatHandler struct {
|
||||||
handler.BaseHandler
|
handler.BaseHandler
|
||||||
redis *redis.Client
|
redis *redis.Client
|
||||||
uploadManager *oss.UploaderManager
|
uploadManager *oss.UploaderManager
|
||||||
licenseService *service.LicenseService
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manager *oss.UploaderManager, licenseService *service.LicenseService) *ChatHandler {
|
func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manager *oss.UploaderManager) *ChatHandler {
|
||||||
return &ChatHandler{
|
return &ChatHandler{
|
||||||
BaseHandler: handler.BaseHandler{App: app, DB: db},
|
BaseHandler: handler.BaseHandler{App: app, DB: db},
|
||||||
redis: redis,
|
redis: redis,
|
||||||
uploadManager: manager,
|
uploadManager: manager,
|
||||||
licenseService: licenseService,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,14 +488,6 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, sessi
|
|||||||
return nil, errors.New("no available key, please import key")
|
return nil, errors.New("no available key, please import key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ONLY allow apiURL in blank list
|
|
||||||
if session.Model.Platform == types.OpenAI {
|
|
||||||
err := h.licenseService.IsValidApiURL(apiKey.ApiURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var apiURL string
|
var apiURL string
|
||||||
switch session.Model.Platform {
|
switch session.Model.Platform {
|
||||||
case types.Azure:
|
case types.Azure:
|
||||||
|
@ -8,14 +8,13 @@ package handler
|
|||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"geekai/core"
|
"geekai/core"
|
||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"geekai/service"
|
|
||||||
"geekai/store/model"
|
"geekai/store/model"
|
||||||
"geekai/store/vo"
|
"geekai/store/vo"
|
||||||
"geekai/utils"
|
"geekai/utils"
|
||||||
"geekai/utils/resp"
|
"geekai/utils/resp"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -29,22 +28,19 @@ import (
|
|||||||
|
|
||||||
type UserHandler struct {
|
type UserHandler struct {
|
||||||
BaseHandler
|
BaseHandler
|
||||||
searcher *xdb.Searcher
|
searcher *xdb.Searcher
|
||||||
redis *redis.Client
|
redis *redis.Client
|
||||||
licenseService *service.LicenseService
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserHandler(
|
func NewUserHandler(
|
||||||
app *core.AppServer,
|
app *core.AppServer,
|
||||||
db *gorm.DB,
|
db *gorm.DB,
|
||||||
searcher *xdb.Searcher,
|
searcher *xdb.Searcher,
|
||||||
client *redis.Client,
|
client *redis.Client) *UserHandler {
|
||||||
licenseService *service.LicenseService) *UserHandler {
|
|
||||||
return &UserHandler{
|
return &UserHandler{
|
||||||
BaseHandler: BaseHandler{DB: db, App: app},
|
BaseHandler: BaseHandler{DB: db, App: app},
|
||||||
searcher: searcher,
|
searcher: searcher,
|
||||||
redis: client,
|
redis: client,
|
||||||
licenseService: licenseService,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,14 +64,6 @@ func (h *UserHandler) Register(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测最大注册人数
|
|
||||||
var totalUser int64
|
|
||||||
h.DB.Model(&model.User{}).Count(&totalUser)
|
|
||||||
if int(totalUser) >= h.licenseService.GetLicense().UserNum {
|
|
||||||
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查验证码
|
// 检查验证码
|
||||||
var key string
|
var key string
|
||||||
if data.RegWay == "email" || data.RegWay == "mobile" {
|
if data.RegWay == "email" || data.RegWay == "mobile" {
|
||||||
|
@ -172,10 +172,6 @@ func main() {
|
|||||||
// 邮件服务
|
// 邮件服务
|
||||||
fx.Provide(service.NewSmtpService),
|
fx.Provide(service.NewSmtpService),
|
||||||
// License 服务
|
// License 服务
|
||||||
fx.Provide(service.NewLicenseService),
|
|
||||||
fx.Invoke(func(licenseService *service.LicenseService) {
|
|
||||||
licenseService.SyncLicense()
|
|
||||||
}),
|
|
||||||
|
|
||||||
// 微信机器人服务
|
// 微信机器人服务
|
||||||
fx.Provide(wx.NewWeChatBot),
|
fx.Provide(wx.NewWeChatBot),
|
||||||
@ -300,8 +296,6 @@ func main() {
|
|||||||
group := s.Engine.Group("/api/admin/")
|
group := s.Engine.Group("/api/admin/")
|
||||||
group.POST("config/update", h.Update)
|
group.POST("config/update", h.Update)
|
||||||
group.GET("config/get", h.Get)
|
group.GET("config/get", h.Get)
|
||||||
group.POST("active", h.Active)
|
|
||||||
group.GET("config/get/license", h.GetLicense)
|
|
||||||
}),
|
}),
|
||||||
fx.Invoke(func(s *core.AppServer, h *admin.ManagerHandler) {
|
fx.Invoke(func(s *core.AppServer, h *admin.ManagerHandler) {
|
||||||
group := s.Engine.Group("/api/admin/")
|
group := s.Engine.Group("/api/admin/")
|
||||||
|
@ -1,195 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
|
||||||
// * Use of this source code is governed by a Apache-2.0 license
|
|
||||||
// * that can be found in the LICENSE file.
|
|
||||||
// * @Author yangjian102621@163.com
|
|
||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"geekai/core"
|
|
||||||
"geekai/core/types"
|
|
||||||
"geekai/store"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/imroc/req/v3"
|
|
||||||
"github.com/shirou/gopsutil/host"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LicenseService struct {
|
|
||||||
config types.ApiConfig
|
|
||||||
levelDB *store.LevelDB
|
|
||||||
license *types.License
|
|
||||||
urlWhiteList []string
|
|
||||||
machineId string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLicenseService(server *core.AppServer, levelDB *store.LevelDB) *LicenseService {
|
|
||||||
var license types.License
|
|
||||||
var machineId string
|
|
||||||
_ = levelDB.Get(types.LicenseKey, &license)
|
|
||||||
info, err := host.Info()
|
|
||||||
if err == nil {
|
|
||||||
machineId = info.HostID
|
|
||||||
}
|
|
||||||
logger.Infof("License: %+v", license)
|
|
||||||
return &LicenseService{
|
|
||||||
config: server.Config.ApiConfig,
|
|
||||||
levelDB: levelDB,
|
|
||||||
license: &license,
|
|
||||||
machineId: machineId,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type License struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
License string `json:"license"`
|
|
||||||
Mid string `json:"mid"`
|
|
||||||
ActiveAt int64 `json:"active_at"`
|
|
||||||
ExpiredAt int64 `json:"expired_at"`
|
|
||||||
UserNum int `json:"user_num"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActiveLicense 激活 License
|
|
||||||
func (s *LicenseService) ActiveLicense(license string, machineId string) error {
|
|
||||||
var res struct {
|
|
||||||
Code types.BizCode `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Data License `json:"data"`
|
|
||||||
}
|
|
||||||
apiURL := fmt.Sprintf("%s/%s", s.config.ApiURL, "api/license/active")
|
|
||||||
response, err := req.C().R().
|
|
||||||
SetBody(map[string]string{"license": license, "machine_id": machineId}).
|
|
||||||
SetSuccessResult(&res).Post(apiURL)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("发送激活请求失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.IsErrorState() {
|
|
||||||
return fmt.Errorf("发送激活请求失败:%v", response.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Code != types.Success {
|
|
||||||
return fmt.Errorf("激活失败:%v", res.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.license = &types.License{
|
|
||||||
Key: license,
|
|
||||||
MachineId: machineId,
|
|
||||||
UserNum: res.Data.UserNum,
|
|
||||||
ExpiredAt: res.Data.ExpiredAt,
|
|
||||||
IsActive: true,
|
|
||||||
}
|
|
||||||
err = s.levelDB.Put(types.LicenseKey, s.license)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("保存许可证书失败:%v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncLicense 定期同步 License
|
|
||||||
func (s *LicenseService) SyncLicense() {
|
|
||||||
go func() {
|
|
||||||
retryCounter := 0
|
|
||||||
for {
|
|
||||||
license, err := s.fetchLicense()
|
|
||||||
if err != nil {
|
|
||||||
retryCounter++
|
|
||||||
if retryCounter < 5 {
|
|
||||||
logger.Error(err)
|
|
||||||
}
|
|
||||||
s.license.IsActive = false
|
|
||||||
} else {
|
|
||||||
s.license = license
|
|
||||||
}
|
|
||||||
|
|
||||||
urls, err := s.fetchUrlWhiteList()
|
|
||||||
if err == nil {
|
|
||||||
s.urlWhiteList = urls
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Second * 10)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LicenseService) fetchLicense() (*types.License, error) {
|
|
||||||
var res struct {
|
|
||||||
Code types.BizCode `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Data License `json:"data"`
|
|
||||||
}
|
|
||||||
apiURL := fmt.Sprintf("%s/%s", s.config.ApiURL, "api/license/check")
|
|
||||||
response, err := req.C().R().
|
|
||||||
SetBody(map[string]string{"license": s.license.Key, "machine_id": s.machineId}).
|
|
||||||
SetSuccessResult(&res).Post(apiURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("发送激活请求失败: %v", err)
|
|
||||||
}
|
|
||||||
if response.IsErrorState() {
|
|
||||||
return nil, fmt.Errorf("激活失败:%v", response.Status)
|
|
||||||
}
|
|
||||||
if res.Code != types.Success {
|
|
||||||
return nil, fmt.Errorf("激活失败:%v", res.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.License{
|
|
||||||
Key: res.Data.License,
|
|
||||||
MachineId: res.Data.Mid,
|
|
||||||
UserNum: res.Data.UserNum,
|
|
||||||
ExpiredAt: res.Data.ExpiredAt,
|
|
||||||
IsActive: true,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LicenseService) fetchUrlWhiteList() ([]string, error) {
|
|
||||||
var res struct {
|
|
||||||
Code types.BizCode `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Data []string `json:"data"`
|
|
||||||
}
|
|
||||||
apiURL := fmt.Sprintf("%s/%s", s.config.ApiURL, "api/license/urls")
|
|
||||||
response, err := req.C().R().SetSuccessResult(&res).Get(apiURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("发送请求失败: %v", err)
|
|
||||||
}
|
|
||||||
if response.IsErrorState() {
|
|
||||||
return nil, fmt.Errorf("发送请求失败:%v", response.Status)
|
|
||||||
}
|
|
||||||
if res.Code != types.Success {
|
|
||||||
return nil, fmt.Errorf("获取白名单失败:%v", res.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLicense 获取许可信息
|
|
||||||
func (s *LicenseService) GetLicense() *types.License {
|
|
||||||
return s.license
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValidApiURL 判断是否合法的中转 URL
|
|
||||||
func (s *LicenseService) IsValidApiURL(uri string) error {
|
|
||||||
// 获得许可授权的直接放行
|
|
||||||
if s.license.IsActive {
|
|
||||||
if s.license.MachineId != s.machineId {
|
|
||||||
return errors.New("系统使用了盗版的许可证书")
|
|
||||||
}
|
|
||||||
|
|
||||||
if time.Now().Unix() > s.license.ExpiredAt {
|
|
||||||
return errors.New("系统许可证书已经过期")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range s.urlWhiteList {
|
|
||||||
if strings.HasPrefix(uri, v) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("当前 API 地址 %s 不在白名单列表当中。", uri)
|
|
||||||
}
|
|
@ -11,7 +11,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
logger2 "geekai/logger"
|
logger2 "geekai/logger"
|
||||||
"geekai/service"
|
|
||||||
"geekai/service/oss"
|
"geekai/service/oss"
|
||||||
"geekai/service/sd"
|
"geekai/service/sd"
|
||||||
"geekai/store"
|
"geekai/store"
|
||||||
@ -34,7 +33,7 @@ type ServicePool struct {
|
|||||||
|
|
||||||
var logger = logger2.GetLogger()
|
var logger = logger2.GetLogger()
|
||||||
|
|
||||||
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig, licenseService *service.LicenseService) *ServicePool {
|
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig) *ServicePool {
|
||||||
services := make([]*Service, 0)
|
services := make([]*Service, 0)
|
||||||
taskQueue := store.NewRedisQueue("MidJourney_Task_Queue", redisCli)
|
taskQueue := store.NewRedisQueue("MidJourney_Task_Queue", redisCli)
|
||||||
notifyQueue := store.NewRedisQueue("MidJourney_Notify_Queue", redisCli)
|
notifyQueue := store.NewRedisQueue("MidJourney_Notify_Queue", redisCli)
|
||||||
@ -43,11 +42,6 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
|||||||
if config.Enabled == false {
|
if config.Enabled == false {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err := licenseService.IsValidApiURL(config.ApiURL)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cli := NewPlusClient(config)
|
cli := NewPlusClient(config)
|
||||||
name := fmt.Sprintf("mj-plus-service-%d", k)
|
name := fmt.Sprintf("mj-plus-service-%d", k)
|
||||||
|
Loading…
Reference in New Issue
Block a user