chore: 初始开源发布

iotlcm-upstream-proxy:渠道缓存与转发网关。

- 内联原私有依赖:yunze-public → internal/pub/(18 文件),
  yunze-pb/pb → internal/pb/(9 文件);Consul 注册改用官方
  gogf/gf/contrib/registry/consul/v2(API 兼容)
- Go module:yz-svr → git.5iot.com/iotlcm-public/iotlcm-upstream-proxy
- 凭据与内部标识脱敏:MySQL/Redis/Consul 凭据、私网与生产 IP、内网域名
- 移除内部资产:CI 触发器
- Dockerfile 改用公共基础镜像,移除写死的 registry 凭据
- 补 Apache-2.0 LICENSE,重写 README,LF 行尾 + .gitattributes
This commit is contained in:
MiFaZhan
2026-10-22 15:46:43 +08:00
commit 82466d587d
110 changed files with 7932 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
package cmd
import (
"context"
"fmt"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/controller/infopb"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/controller/msgcallpb"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubconsts"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubservice"
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gsvc"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/util/gconv"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
const (
ServiceNamePrefix = "iotlcmupstreamproxy"
)
var (
gGrpcPort = 8700
gAccessIP = "0.0.0.0"
)
var (
Main = gcmd.Command{
Name: "main",
Usage: "main",
Brief: "start http server",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
s := g.Server()
gGrpcPort = gconv.Int(gcmd.GetOptWithEnv("grpcport"))
gAccessIP = gconv.String(gcmd.GetOptWithEnv("accessip"))
if len(gAccessIP) == 0 {
gAccessIP = "0.0.0.0"
}
if gGrpcPort == 0 {
gGrpcPort = 8110
}
nodeName := fmt.Sprintf("%s", ServiceNamePrefix)
xjpubservice.Info().SetInfoName(xjpubconsts.ServiceTypeAccess, fmt.Sprintf("%s-http-svc", ServiceNamePrefix), "上游运营商渠道服务", "上游运营商渠道服务")
xjpubservice.Info().AddInfoData("grpcport", gGrpcPort)
xjpubservice.Info().AddInfoData("accessip", gAccessIP)
xjpubservice.Info().AddInfoData("nodeName", nodeName)
xjpubservice.InfoRpc().SetInfoName(xjpubconsts.ServiceTypeAccess, fmt.Sprintf("%s-grpc-svc", ServiceNamePrefix), "上游运营商渠道服务", "上游运营商渠道服务")
xjpubservice.InfoRpc().AddInfoData("grpcport", gGrpcPort)
xjpubservice.InfoRpc().AddInfoData("accessip", gAccessIP)
xjpubservice.InfoRpc().AddInfoData("nodeName", nodeName)
bindHTTPRoutes(s)
err = service.ChannelConfig().Start(ctx)
if err != nil {
return err
}
err = service.UpstreamAPI().Start(ctx)
if err != nil {
return err
}
go func() {
c := grpcx.Server.NewConfig()
c.Endpoints = []string{fmt.Sprintf("%s:%d", gAccessIP, gGrpcPort)}
c.Options = append(c.Options, []grpc.ServerOption{
grpcx.Server.ChainUnary(
grpcx.Server.UnaryValidate,
),
}...)
sGRPC := grpcx.Server.New(c)
sGRPC.Service(&gsvc.LocalService{
Name: fmt.Sprintf("%s-grpc-svc", ServiceNamePrefix),
Endpoints: gsvc.Endpoints{gsvc.NewEndpoint(fmt.Sprintf("%s:%d", gAccessIP, gGrpcPort))},
Metadata: gsvc.Metadata{
gsvc.MDProtocol: "grpc",
gsvc.MDInsecure: true,
},
})
infopb.Register(sGRPC)
msgcallpb.Register(sGRPC)
reflection.Register(sGRPC.Server)
g.Log().Infof(ctx, "GRPC Server启动, listenPort:%d", gGrpcPort)
sGRPC.Run()
g.Log().Infof(ctx, "GRPC Server堵塞运行结束")
}()
s.Run()
service.UpstreamAPI().Stop(ctx)
service.ChannelConfig().Stop(ctx)
return nil
},
}
)
+19
View File
@@ -0,0 +1,19 @@
package cmd
import (
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/controller/cardquery"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/controller/channel"
"github.com/gogf/gf/v2/net/ghttp"
)
func bindHTTPRoutes(server *ghttp.Server) {
server.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ghttp.MiddlewareHandlerResponse)
group.Bind(
cardquery.NewV1(),
channel.NewV1(),
)
group.POST("/internal/app-diagnosis/sim/*any", cardquery.AppDiagnosisQuery)
})
}
+38
View File
@@ -0,0 +1,38 @@
package cmd
import (
"strings"
"testing"
"github.com/gogf/gf/v2/net/ghttp"
)
func TestBindHTTPRoutesRegistersChannelControlRoutes(t *testing.T) {
server := ghttp.GetServer("proxy-channel-refresh-route-test")
server.SetAddr("127.0.0.1:0")
bindHTTPRoutes(server)
if err := server.Start(); err != nil {
t.Fatalf("start HTTP server: %v", err)
}
t.Cleanup(func() {
if err := server.Shutdown(); err != nil {
t.Errorf("shutdown HTTP server: %v", err)
}
})
want := map[string]bool{
"/internal/channel/refresh": false,
"/internal/channel/remove": false,
"/internal/app-diagnosis/sim/*any": false,
}
for _, route := range server.GetRoutes() {
if _, ok := want[route.Route]; ok && strings.EqualFold(route.Method, "POST") {
want[route.Route] = true
}
}
for route, found := range want {
if !found {
t.Fatalf("POST %s route was not registered: %+v", route, server.GetRoutes())
}
}
}
+6
View File
@@ -0,0 +1,6 @@
package consts
const (
ApiStatusSuccess = 0
ApiStatusError = 9999
)
@@ -0,0 +1,5 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package cardquery
@@ -0,0 +1,79 @@
package cardquery
import (
"crypto/subtle"
"encoding/json"
"net/http"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
var appDiagnosisQueryPaths = map[string]bool{
"/sim/query/sim-basic-info": true,
"/sim/query/sim-status": true,
"/sim/query/sim-session": true,
"/sim/query/sim-apn-info": true,
"/sim/query/sim-imei": true,
"/sim/query/sim-device-status": true,
"/sim/query/sim-card-bind-status": true,
"/sim/query/sim-data-diagnosis": true,
"/sim/query/sim-stop-reason": true,
"/sim/query/sim-frozen-state": true,
"/sim/query/sim-datausage": true,
"/sim/query/sim-region-limit-status": true,
}
// AppDiagnosisQuery bypasses the proxy's local card lookup only for verified
// internal, read-only diagnosis requests. The existing /sim/* API is untouched.
func AppDiagnosisQuery(r *ghttp.Request) {
secret := strings.TrimSpace(g.Cfg().MustGet(r.Context(), "appDiagnosis.internalToken").String())
provided := r.Header.Get("X-App-Diagnosis-Token")
if secret == "" || subtle.ConstantTimeCompare([]byte(secret), []byte(provided)) != 1 {
appDiagnosisReject(r, http.StatusForbidden, "内部查询未授权")
return
}
path := strings.TrimPrefix(r.URL.Path, "/internal/app-diagnosis")
if !appDiagnosisQueryPaths[path] {
appDiagnosisReject(r, http.StatusNotFound, "诊断接口不存在")
return
}
if len(r.GetBody()) > 64*1024 {
appDiagnosisReject(r, http.StatusRequestEntityTooLarge, "请求内容过大")
return
}
var input model.SimBaseReqJson
if err := json.Unmarshal(r.GetBody(), &input); err != nil || len(input.SimKeys) != 1 || input.SimKeys[0] == nil {
appDiagnosisReject(r, http.StatusBadRequest, "诊断卡参数无效")
return
}
key := input.SimKeys[0]
key.ChanKey = strings.TrimSpace(key.ChanKey)
if key.ChanKey == "" || strings.TrimSpace(key.ICCID) == "" {
appDiagnosisReject(r, http.StatusBadRequest, "诊断卡通道或ICCID缺失")
return
}
// Channel credentials and gateway routing are loaded by UpstreamAPI from
// this proxy's own configured channel, never from the legacy database.
channel, ok := service.UpstreamAPI().GetChan(key.ChanKey)
if !ok || channel == nil || channel.Instance == nil {
appDiagnosisReject(r, http.StatusNotFound, "诊断通道不可用")
return
}
r.URL.Path = path
data, err := service.UpstreamAPI().SimBase(r.Context(), &input)
if err != nil {
r.SetError(err)
return
}
r.Response.WriteJson(g.Map{"code": 0, "data": data})
}
func appDiagnosisReject(r *ghttp.Request, status int, message string) {
r.Response.WriteStatus(status)
r.Response.WriteJson(g.Map{"code": status, "message": message})
}
@@ -0,0 +1,55 @@
package cardquery
import (
"net/http"
"strings"
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcfg"
)
func TestDiagnosisProxyRejectsUntrustedAndInvalidRequests(t *testing.T) {
adapter, err := gcfg.NewAdapterContent(`{"appDiagnosis":{"internalToken":"test-token"}}`)
if err != nil {
t.Fatal(err)
}
previous := g.Cfg().GetAdapter()
g.Cfg().SetAdapter(adapter)
t.Cleanup(func() { g.Cfg().SetAdapter(previous) })
server := ghttp.GetServer("diagnosis-proxy-auth-test")
server.SetAddr("127.0.0.1:0")
server.Group("/", func(group *ghttp.RouterGroup) {
group.POST("/internal/app-diagnosis/sim/*any", AppDiagnosisQuery)
})
if err := server.Start(); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = server.Shutdown() })
url := "http://" + server.GetListenedAddress() + "/internal/app-diagnosis"
for _, tc := range []struct {
path string
token string
body string
status int
}{
{"/sim/query/sim-basic-info", "", `{"simKeys":[{"iccid":"123","chanKey":"test"}]}`, http.StatusForbidden},
{"/sim/restart", "test-token", `{"simKeys":[{"iccid":"123","chanKey":"test"}]}`, http.StatusNotFound},
{"/sim/query/sim-basic-info", "test-token", `{"simKeys":[{"iccid":"123"}]}`, http.StatusBadRequest},
} {
request, err := http.NewRequest(http.MethodPost, url+tc.path, strings.NewReader(tc.body))
if err != nil {
t.Fatal(err)
}
request.Header.Set("X-App-Diagnosis-Token", tc.token)
response, err := http.DefaultClient.Do(request)
if err != nil {
t.Fatal(err)
}
_ = response.Body.Close()
if response.StatusCode != tc.status {
t.Errorf("%s: got %d want %d", tc.path, response.StatusCode, tc.status)
}
}
}
@@ -0,0 +1,15 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package cardquery
import (
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/api/cardquery"
)
type ControllerV1 struct{}
func NewV1() cardquery.ICardqueryV1 {
return &ControllerV1{}
}
@@ -0,0 +1,54 @@
package cardquery
import (
"context"
v1 "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/api/cardquery/v1"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
)
// func (c *ControllerV1) SimBase(ctx context.Context, req *v1.SimBaseReq) (res *v1.SimBaseRes, err error) {
// g.Log().Debugf(ctx, "SimBase req %+v", req)
// r := g.RequestFromCtx(ctx)
// if r.Method != "POST" {
// return nil, gerror.New("method not allowed")
// }
// path := r.URL.Path
// if path == "" {
// return nil, gerror.New("path is required")
// }
// // 获取渠道KEY
// for _, simKey := range req.SimKeys {
// if simKey.ChanKey == "" {
// simKey.ChanKey = "channel_003"
// }
// }
// // 获取三码信息
// url := "http://iotlcmupstreamproxy-http-svc" + path
// client := g.Client()
// client.SetDiscovery(gsvc.GetRegistry())
// client.SetTimeout(time.Second * 10)
// postData, err := json.Marshal(req)
// if err != nil {
// return nil, err
// }
// resp, err := client.Post(ctx, url, postData)
// if err != nil {
// return nil, err
// }
// body := resp.ReadAllString()
// var simBaseRes model.SimBaseResJson
// err = json.Unmarshal([]byte(body), &simBaseRes)
// if err != nil {
// return nil, err
// }
// return &simBaseRes, nil
// }
func (c *ControllerV1) SimBase(ctx context.Context, req *v1.SimBaseReq) (res *v1.SimBaseRes, err error) {
out, err := service.SimKeyFill().SimKeyFill(ctx, &req.SimBaseReqJson)
if err != nil {
return nil, err
}
return &out, nil
}
+9
View File
@@ -0,0 +1,9 @@
package channel
import "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/api/channel"
type ControllerV1 struct{}
func NewV1() channel.IChannelV1 {
return &ControllerV1{}
}
@@ -0,0 +1,25 @@
package channel
import (
"context"
v1 "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/api/channel/v1"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
)
func (c *ControllerV1) Refresh(ctx context.Context, req *v1.RefreshReq) (res *v1.RefreshRes, err error) {
if err = service.UpstreamAPI().RefreshChannel(ctx, req.ChanKey); err != nil {
return nil, err
}
return &v1.RefreshRes{
ChanKey: req.ChanKey,
Refreshed: true,
}, nil
}
func (c *ControllerV1) Remove(ctx context.Context, req *v1.RemoveReq) (res *v1.RemoveRes, err error) {
if err = service.UpstreamAPI().RemoveChannel(ctx, req.ChanKey); err != nil {
return nil, err
}
return &v1.RemoveRes{ChanKey: req.ChanKey, Removed: true}, nil
}
+35
View File
@@ -0,0 +1,35 @@
package infopb
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pb"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubservice"
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"github.com/gogf/gf/v2/frame/g"
)
type Controller struct {
pb.UnimplementedInfoServer
}
func Register(s *grpcx.GrpcServer) {
pb.RegisterInfoServer(s.Server, &Controller{})
}
func (*Controller) Info(ctx context.Context, req *pb.InfoReq) (*pb.InfoRes, error) {
g.Log().Info(ctx, req)
info := xjpubservice.InfoRpc().GetInfo()
return &pb.InfoRes{
Head: &pb.HeadRes{
Code: 0,
Message: "success",
},
Type: info.Type,
Name: info.Name,
Title: info.Title,
Description: info.Description,
Version: info.Version,
}, nil
}
@@ -0,0 +1,47 @@
package msgcallpb
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pb"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubservice"
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"github.com/gogf/gf/v2/frame/g"
)
type Controller struct {
pb.UnimplementedMsgCallServer
}
func Register(s *grpcx.GrpcServer) {
pb.RegisterMsgCallServer(s.Server, &Controller{})
}
func (c *Controller) OneCall(ctx context.Context, req *pb.MsgReq) (res *pb.MsgRes, err error) {
res = &pb.MsgRes{
Head: &pb.HeadRes{
Code: -1,
Message: "service not found",
},
Service: req.Service,
Method: req.Method,
}
if req.Head != nil {
res.Head.RouterId = req.Head.RouterId
res.Head.TraceId = req.Head.TraceId
}
info := xjpubservice.InfoRpc().GetInfo()
if req.Service != info.Name {
g.Log().Errorf(ctx, "OneCall Service not found: service: %s, info: %s", req.Service, info.Name)
return res, nil
}
{
g.Log().Errorf(ctx, "OneCall Method not found: method: %s", req.Method)
res.Head.Code = -1
res.Head.Message = "method not found"
return res, nil
}
return res, nil
}
+187
View File
@@ -0,0 +1,187 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// TSimCardInfoDao is the data access object for the table t_sim_card_info.
type TSimCardInfoDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns TSimCardInfoColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// TSimCardInfoColumns defines and stores column names for the table t_sim_card_info.
type TSimCardInfoColumns struct {
Id string // 主键ID
Iccid string // ICCID
Msisdn string // MSISDN/卡号
Imsi string // IMSI
Imei string // IMEI
RegionCode string // 归属地
DeptId string // 渠道客户ID
CurrentPackageCode string // 当前套餐编号
PackageStatus string // 套餐状态:0 无套餐,1待生效,2生效中,3已过期
AccessType string // 接入方式:ipv4、ipv6、ipv4_ipv6
BossCardStatus string // 运营商卡状态 枚举
CardType string // 卡类型 枚举
BossOpenAt string // 运营商开户日期
BossActivateAt string // 运营商激活日期
IsBossActivateEnabled string // 是否按运营商激活减去赠送时长方:1是,0否
InboundBatchNo string // 入库批次
OutboundBatchNo string // 出库批次
AllocationBatchNo string // 划拨批次
IsFlowPool string // 是否为池卡:1是,0否
FlowPoolCode string // 流量池编号
FlowPkgBeginAt string // 当前套餐生效时间
FlowPkgPresentAt string // 套餐赠送结束时间
FlowPkgRealPresentAt string // 实际赠送结束时间
FlowPkgEndAt string // 当前套餐预计到期时间
DataServiceStatus string // 数据服务状态:1启用、0禁用
PackageTotalFlowMb string // 套餐总流量MB
UsedFlowMb string // 已用流量MB
TrafficCutoffStatus string // 达量断网配置状态:0未配置,1已开启,2已关闭
TrafficCutoffThreshold string // 达量断网阈值
SmsServiceStatus string // 短信服务状态:1启用、0禁用
PackageTotalSms string // 套餐短信总条数
UsedSms string // 已用短信条数
VoiceServiceStatus string // 语音服务状态:1启用、0禁用
PackageTotalVoice string // 套餐语音总分钟数
UsedVoice string // 已用语音分钟数
RoamServiceStatus string // 国漫服务状态:1启用、0禁用
PackageTotalRoamFlowMb string // 套餐国际流量MB
UsedRoamFlowMb string // 已用国际流量MB
StatusSyncTime string // 状态最后一次同步时间
FlowSyncTime string // 流量最后一次同步时间
SmsSyncTime string // 短信最后一次同步时间
VoiceSyncTime string // 语音最后一次同步时间
RoamSyncTime string // 国漫最后一次同步时间
ExtraFeeEnabled string // 是否收取附加费:1是,0否
ExtraFeeMultiple string // 附加费用倍数
ExtraFee string // 附加金额
ChanKey string // 通道编号
NetModel string // 网络类型:4G、5G、NB-IoT
Remark string // 备注
TenantId string // 租户ID
CreatedDept string // 创建部门
CreatedBy string // 创建者
CreatedAt string // 创建时间
UpdatedBy string // 更新者
UpdatedAt string // 更新时间
}
// tSimCardInfoColumns holds the columns for the table t_sim_card_info.
var tSimCardInfoColumns = TSimCardInfoColumns{
Id: "id",
Iccid: "iccid",
Msisdn: "msisdn",
Imsi: "imsi",
Imei: "imei",
RegionCode: "region_code",
DeptId: "dept_id",
CurrentPackageCode: "current_package_code",
PackageStatus: "package_status",
AccessType: "access_type",
BossCardStatus: "boss_card_status",
CardType: "card_type",
BossOpenAt: "boss_open_at",
BossActivateAt: "boss_activate_at",
IsBossActivateEnabled: "is_boss_activate_enabled",
InboundBatchNo: "inbound_batch_no",
OutboundBatchNo: "outbound_batch_no",
AllocationBatchNo: "allocation_batch_no",
IsFlowPool: "is_flow_pool",
FlowPoolCode: "flow_pool_code",
FlowPkgBeginAt: "flow_pkg_begin_at",
FlowPkgPresentAt: "flow_pkg_present_at",
FlowPkgRealPresentAt: "flow_pkg_real_present_at",
FlowPkgEndAt: "flow_pkg_end_at",
DataServiceStatus: "data_service_status",
PackageTotalFlowMb: "package_total_flow_mb",
UsedFlowMb: "used_flow_mb",
TrafficCutoffStatus: "traffic_cutoff_status",
TrafficCutoffThreshold: "traffic_cutoff_threshold",
SmsServiceStatus: "sms_service_status",
PackageTotalSms: "package_total_sms",
UsedSms: "used_sms",
VoiceServiceStatus: "voice_service_status",
PackageTotalVoice: "package_total_voice",
UsedVoice: "used_voice",
RoamServiceStatus: "roam_service_status",
PackageTotalRoamFlowMb: "package_total_roam_flow_mb",
UsedRoamFlowMb: "used_roam_flow_mb",
StatusSyncTime: "status_sync_time",
FlowSyncTime: "flow_sync_time",
SmsSyncTime: "sms_sync_time",
VoiceSyncTime: "voice_sync_time",
RoamSyncTime: "roam_sync_time",
ExtraFeeEnabled: "extra_fee_enabled",
ExtraFeeMultiple: "extra_fee_multiple",
ExtraFee: "extra_fee",
ChanKey: "chan_key",
NetModel: "net_model",
Remark: "remark",
TenantId: "tenant_id",
CreatedDept: "created_dept",
CreatedBy: "created_by",
CreatedAt: "created_at",
UpdatedBy: "updated_by",
UpdatedAt: "updated_at",
}
// NewTSimCardInfoDao creates and returns a new DAO object for table data access.
func NewTSimCardInfoDao(handlers ...gdb.ModelHandler) *TSimCardInfoDao {
return &TSimCardInfoDao{
group: "default",
table: "t_sim_card_info",
columns: tSimCardInfoColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *TSimCardInfoDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *TSimCardInfoDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *TSimCardInfoDao) Columns() TSimCardInfoColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *TSimCardInfoDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *TSimCardInfoDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *TSimCardInfoDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
@@ -0,0 +1,101 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// TSimChannelTemplateDao is the data access object for the table t_sim_channel_template.
type TSimChannelTemplateDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns TSimChannelTemplateColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// TSimChannelTemplateColumns defines and stores column names for the table t_sim_channel_template.
type TSimChannelTemplateColumns struct {
Id string // ID
TempKey string // 模板标识
TempName string // 模板名称
TempModel string // 模板模型
TempConfig string // 模板配置
TempStatus string // 模板状态(0未启用,1启用)
TenantId string // 租户ID
CreatedDept string // 创建部门
CreatedBy string // 创建者
CreatedAt string // 创建时间
UpdatedBy string // 更新者
UpdatedAt string // 更新时间
}
// tSimChannelTemplateColumns holds the columns for the table t_sim_channel_template.
var tSimChannelTemplateColumns = TSimChannelTemplateColumns{
Id: "id",
TempKey: "temp_key",
TempName: "temp_name",
TempModel: "temp_model",
TempConfig: "temp_config",
TempStatus: "temp_status",
TenantId: "tenant_id",
CreatedDept: "created_dept",
CreatedBy: "created_by",
CreatedAt: "created_at",
UpdatedBy: "updated_by",
UpdatedAt: "updated_at",
}
// NewTSimChannelTemplateDao creates and returns a new DAO object for table data access.
func NewTSimChannelTemplateDao(handlers ...gdb.ModelHandler) *TSimChannelTemplateDao {
return &TSimChannelTemplateDao{
group: "default",
table: "t_sim_channel_template",
columns: tSimChannelTemplateColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *TSimChannelTemplateDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *TSimChannelTemplateDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *TSimChannelTemplateDao) Columns() TSimChannelTemplateColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *TSimChannelTemplateDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *TSimChannelTemplateDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *TSimChannelTemplateDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
+117
View File
@@ -0,0 +1,117 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// TSimChannelsDao is the data access object for the table t_sim_channels.
type TSimChannelsDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns TSimChannelsColumns // columns contains all the column names of Table for convenient usage.
handlers []gdb.ModelHandler // handlers for customized model modification.
}
// TSimChannelsColumns defines and stores column names for the table t_sim_channels.
type TSimChannelsColumns struct {
Id string // ID
TempKey string // 模板标识
ChanKey string // 通道编号
ChanName string // 通道名称
ChanConfig string // 通道配置
ChanStatus string // 通道状态(0未启用,1启用)
ChanAlias string // 通道别名
RegionCode string // 通道行政区域代码(省市)
AppKey string // 应用标识
AppSecret string // 应用密钥
AccessToken string // 接口调用凭证
AccessTokenCreated string // 凭证申请时间
AccessTokenExpire string // 凭证过期时间
RefreshToken string // 刷新凭证
TenantId string // 租户ID
CreatedDept string // 创建部门
CreatedBy string // 创建者
CreatedAt string // 创建时间
UpdatedBy string // 更新者
UpdatedAt string // 更新时间
}
// tSimChannelsColumns holds the columns for the table t_sim_channels.
var tSimChannelsColumns = TSimChannelsColumns{
Id: "id",
TempKey: "temp_key",
ChanKey: "chan_key",
ChanName: "chan_name",
ChanConfig: "chan_config",
ChanStatus: "chan_status",
ChanAlias: "chan_alias",
RegionCode: "region_code",
AppKey: "app_key",
AppSecret: "app_secret",
AccessToken: "access_token",
AccessTokenCreated: "access_token_created",
AccessTokenExpire: "access_token_expire",
RefreshToken: "refresh_token",
TenantId: "tenant_id",
CreatedDept: "created_dept",
CreatedBy: "created_by",
CreatedAt: "created_at",
UpdatedBy: "updated_by",
UpdatedAt: "updated_at",
}
// NewTSimChannelsDao creates and returns a new DAO object for table data access.
func NewTSimChannelsDao(handlers ...gdb.ModelHandler) *TSimChannelsDao {
return &TSimChannelsDao{
group: "default",
table: "t_sim_channels",
columns: tSimChannelsColumns,
handlers: handlers,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *TSimChannelsDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *TSimChannelsDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *TSimChannelsDao) Columns() TSimChannelsColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *TSimChannelsDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *TSimChannelsDao) Ctx(ctx context.Context) *gdb.Model {
model := dao.DB().Model(dao.table)
for _, handler := range dao.handlers {
model = handler(model)
}
return model.Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *TSimChannelsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
+22
View File
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/dao/internal"
)
// tSimCardInfoDao is the data access object for the table t_sim_card_info.
// You can define custom methods on it to extend its functionality as needed.
type tSimCardInfoDao struct {
*internal.TSimCardInfoDao
}
var (
// TSimCardInfo is a globally accessible object for table t_sim_card_info operations.
TSimCardInfo = tSimCardInfoDao{internal.NewTSimCardInfoDao()}
)
// Add your custom methods and functionality below.
+22
View File
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/dao/internal"
)
// tSimChannelTemplateDao is the data access object for the table t_sim_channel_template.
// You can define custom methods on it to extend its functionality as needed.
type tSimChannelTemplateDao struct {
*internal.TSimChannelTemplateDao
}
var (
// TSimChannelTemplate is a globally accessible object for table t_sim_channel_template operations.
TSimChannelTemplate = tSimChannelTemplateDao{internal.NewTSimChannelTemplateDao()}
)
// Add your custom methods and functionality below.
+22
View File
@@ -0,0 +1,22 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/dao/internal"
)
// tSimChannelsDao is the data access object for the table t_sim_channels.
// You can define custom methods on it to extend its functionality as needed.
type tSimChannelsDao struct {
*internal.TSimChannelsDao
}
var (
// TSimChannels is a globally accessible object for table t_sim_channels operations.
TSimChannels = tSimChannelsDao{internal.NewTSimChannelsDao()}
)
// Add your custom methods and functionality below.
@@ -0,0 +1,259 @@
package channelconfig
import (
"context"
"database/sql"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/dao"
"sync"
"time"
"github.com/gogf/gf/v2/crypto/gmd5"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
)
// channelConfigRow extends the generated entity without losing SQL NULL values.
type channelConfigRow struct {
entity.TSimChannels
IsNormal *int `json:"isNormal" orm:"is_normal"`
}
type sChannelConfig struct {
sync.RWMutex
channelCache map[string]*model.SimChannelConfigItemCache
loadVersion uint8
chgLogMaxId int64
running bool
}
func NewChannelConfig() *sChannelConfig {
return &sChannelConfig{
channelCache: make(map[string]*model.SimChannelConfigItemCache),
}
}
func init() {
service.RegisterChannelConfig(NewChannelConfig())
}
func (s *sChannelConfig) Start(ctx context.Context) error {
err := s.LoadAllChannelConfig(ctx)
if err != nil {
g.Log().Errorf(ctx, "ChannelConfig Start err %+v", err)
return err
}
s.running = true
err = s.Loop(ctx)
if err != nil {
g.Log().Errorf(ctx, "ChannelConfig Start err %+v", err)
return err
}
return nil
}
func (s *sChannelConfig) Stop(ctx context.Context) error {
s.running = false
return nil
}
func (s *sChannelConfig) Loop(ctx context.Context) error {
g.Log().Infof(ctx, "ChannelConfig loop begin.")
lastLoadAllChannelConfigTime := int64(0)
go func() {
for s.running {
now := time.Now().Unix()
loadAllInterval := g.Cfg().MustGet(ctx, "channel.load_all_interval", 2*60*60).Int64()
if now-lastLoadAllChannelConfigTime >= loadAllInterval {
lastLoadAllChannelConfigTime = now
s.LoadAllChannelConfig(ctx)
}
time.Sleep(1 * time.Second)
}
}()
g.Log().Infof(ctx, "ChannelConfig loop background goroutine started.")
return nil
}
func (s *sChannelConfig) LoadAllChannelConfig(ctx context.Context) error {
s.loadVersion++
const warnRowThreshold = 1000
dbSimChannels := make([]*channelConfigRow, 0)
err := dao.TSimChannels.Ctx(ctx).
OrderAsc(dao.TSimChannels.Columns().Id).
Scan(&dbSimChannels)
if err != nil && err != sql.ErrNoRows {
return err
}
if len(dbSimChannels) >= warnRowThreshold {
g.Log().Warningf(ctx, "LoadAllChannelConfig TSimChannels row count %d >= %d, confirm small-table assumption or add pagination",
len(dbSimChannels), warnRowThreshold)
}
dbSimChannelTemplates := make([]*entity.TSimChannelTemplate, 0)
err = dao.TSimChannelTemplate.Ctx(ctx).
OrderAsc(dao.TSimChannelTemplate.Columns().Id).
Scan(&dbSimChannelTemplates)
if err != nil && err != sql.ErrNoRows {
return err
}
if len(dbSimChannelTemplates) >= warnRowThreshold {
g.Log().Warningf(ctx, "LoadAllChannelConfig TSimChannelTemplate row count %d >= %d, confirm small-table assumption or add pagination",
len(dbSimChannelTemplates), warnRowThreshold)
}
templateByKey := make(map[string]*entity.TSimChannelTemplate, len(dbSimChannelTemplates))
for _, tpl := range dbSimChannelTemplates {
if tpl != nil {
templateByKey[tpl.TempKey] = tpl
}
}
s.Lock()
for _, dbSimChannel := range dbSimChannels {
if dbSimChannel == nil {
continue
}
cache := &model.SimChannelConfigItemCache{
Channel: &dbSimChannel.TSimChannels,
IsNormal: dbSimChannel.IsNormal,
Version: s.getChanVersion(ctx, dbSimChannel),
}
oldChannelCache, _ := s.channelCache[dbSimChannel.ChanKey]
if cache.CanCreateInstance() {
template, ok := templateByKey[dbSimChannel.TempKey]
if !ok {
g.Log().Warningf(ctx, "LoadAllChannelConfig no template for tempKey=%s chanKey=%s", dbSimChannel.TempKey, dbSimChannel.ChanKey)
continue
}
cache.Template = template
cache.Version += s.getTemplateVersion(ctx, template)
}
if oldChannelCache != nil && oldChannelCache.Version == cache.Version {
continue
}
s.channelCache[dbSimChannel.ChanKey] = cache
}
s.Unlock()
return nil
}
func (s *sChannelConfig) RefreshChannel(ctx context.Context, chanKey string) error {
if chanKey == "" {
return gerror.New("chanKey is required")
}
channel := new(channelConfigRow)
if err := dao.TSimChannels.Ctx(ctx).Where(dao.TSimChannels.Columns().ChanKey, chanKey).Scan(channel); err != nil {
return err
}
if channel.ChanKey == "" {
return gerror.Newf("channel not found: %s", chanKey)
}
cache := &model.SimChannelConfigItemCache{
Channel: &channel.TSimChannels,
IsNormal: channel.IsNormal,
Version: s.getChanVersion(ctx, channel),
}
// Ineligible channels refresh successfully even while their template is incomplete.
if cache.CanCreateInstance() {
template := new(entity.TSimChannelTemplate)
if err := dao.TSimChannelTemplate.Ctx(ctx).Where(dao.TSimChannelTemplate.Columns().TempKey, channel.TempKey).Scan(template); err != nil {
return err
}
if template.TempKey == "" {
return gerror.Newf("channel template not found: %s", channel.TempKey)
}
cache.Template = template
cache.Version += s.getTemplateVersion(ctx, template)
}
s.Lock()
s.channelCache[chanKey] = cache
s.Unlock()
return nil
}
// RemoveChannel removes a channel configuration cache. Missing keys are
// intentionally treated as success so rename retries remain idempotent.
func (s *sChannelConfig) RemoveChannel(ctx context.Context, chanKey string) error {
chanKey = strings.TrimSpace(chanKey)
if chanKey == "" {
return gerror.New("chanKey is required")
}
s.Lock()
delete(s.channelCache, chanKey)
s.Unlock()
g.Log().Infof(ctx, "channel config removed, chanKey: %s", chanKey)
return nil
}
// get alarm cache by id
func (s *sChannelConfig) GetChannel(chanKey string) (*model.SimChannelConfigItemCache, bool) {
s.RLock()
defer s.RUnlock()
channelCache, ok := s.channelCache[chanKey]
return channelCache, ok
}
func (s *sChannelConfig) GetAllChannel(ctx context.Context) ([]*model.SimChannelConfigItemCache, error) {
s.RLock()
defer s.RUnlock()
channels := make([]*model.SimChannelConfigItemCache, 0)
for _, channelCache := range s.channelCache {
channels = append(channels, channelCache)
}
return channels, nil
}
func (s *sChannelConfig) getChanVersion(ctx context.Context, channel *channelConfigRow) string {
chanKey := channel.ChanKey
channelNew := &channelConfigRow{
IsNormal: channel.IsNormal,
TSimChannels: entity.TSimChannels{
TempKey: channel.TempKey,
ChanKey: channel.ChanKey,
ChanName: channel.ChanName,
ChanConfig: channel.ChanConfig,
ChanStatus: channel.ChanStatus,
ChanAlias: channel.ChanAlias,
RegionCode: channel.RegionCode,
AppKey: channel.AppKey,
AppSecret: channel.AppSecret,
AccessToken: channel.AccessToken,
RefreshToken: channel.RefreshToken,
},
}
chanVersion, err := gmd5.EncryptString(gjson.MustEncodeString(channelNew))
if err != nil {
g.Log().Errorf(ctx, "sChannelConfig.getChanVersion encrypt error %+v, chanKey %s", err, chanKey)
return ""
}
g.Log().Debugf(ctx, "sChannelConfig.getChanVersion chanVersion=%s chanKey=%s", chanVersion, chanKey)
return chanVersion
}
func (s *sChannelConfig) getTemplateVersion(ctx context.Context, template *entity.TSimChannelTemplate) string {
templateKey := template.TempKey
templateNew := &entity.TSimChannelTemplate{
TempKey: template.TempKey,
TempName: template.TempName,
TempModel: template.TempModel,
TempConfig: template.TempConfig,
TempStatus: template.TempStatus,
}
templateVersion, err := gmd5.EncryptString(gjson.MustEncodeString(templateNew))
if err != nil {
g.Log().Errorf(ctx, "sChannelConfig.getTemplateVersion encrypt error %+v, templateKey %s, templateNew %+v", err, templateKey, templateNew)
return ""
}
g.Log().Debugf(ctx, "sChannelConfig.getTemplateVersion templateVersion=%s templateKey=%s", templateVersion, templateKey)
return templateVersion
}
@@ -0,0 +1,37 @@
package channelconfig
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
"testing"
)
func TestChannelVersionDetectsCredentialChanges(t *testing.T) {
normal := 0
row := &channelConfigRow{IsNormal: &normal, TSimChannels: entity.TSimChannels{
ChanKey: "telecom-test", TempKey: "sim_dx_v1", AppKey: "key", AppSecret: "secret",
AccessToken: "token", RefreshToken: "refresh",
}}
config := NewChannelConfig()
before := config.getChanVersion(context.Background(), row)
for _, field := range []string{"appKey", "appSecret", "accessToken", "refreshToken", "tempKey"} {
t.Run(field, func(t *testing.T) {
changed := *row
switch field {
case "appKey":
changed.AppKey = "new-key"
case "appSecret":
changed.AppSecret = "new-secret"
case "accessToken":
changed.AccessToken = "new-token"
case "refreshToken":
changed.RefreshToken = "new-refresh"
case "tempKey":
changed.TempKey = "sim_dx_v3"
}
if got := config.getChanVersion(context.Background(), &changed); got == before {
t.Fatalf("%s change did not invalidate runtime version", field)
}
})
}
}
@@ -0,0 +1,30 @@
package channelconfig
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"github.com/gogf/gf/v2/frame/g"
)
type sChannelUpdate struct{}
func NewChannelUpdate() *sChannelUpdate {
return &sChannelUpdate{}
}
func init() {
service.RegisterChannelUpdate(NewChannelUpdate())
}
func (s *sChannelUpdate) UpdateAccessToken(ctx context.Context, input *model.UpdateAccessTokenInput) error {
_, err := dao.TSimChannels.Ctx(ctx).Where(dao.TSimChannels.Columns().ChanKey, input.ChanKey).Update(g.Map{
dao.TSimChannels.Columns().AccessToken: input.AccessToken,
dao.TSimChannels.Columns().AccessTokenCreated: input.AccessTokenCreated,
dao.TSimChannels.Columns().AccessTokenExpire: input.AccessTokenExpire,
dao.TSimChannels.Columns().RefreshToken: input.RefreshToken,
})
return err
}
+12
View File
@@ -0,0 +1,12 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package logic
import (
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/channelconfig"
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/simKeyFill"
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/upstreamapi"
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/upstreamimpl"
)
+177
View File
@@ -0,0 +1,177 @@
package simKeyFill
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
)
type sSimKeyFill struct {
}
func NewSimKeyFill() *sSimKeyFill {
return &sSimKeyFill{}
}
func init() {
service.RegisterSimKeyFill(NewSimKeyFill())
}
func (s *sSimKeyFill) SimKeyFill(ctx context.Context, input *model.SimBaseReqJson) (output model.SimBaseResJson, err error) {
simKeys, err := s.batchGetSimKey(ctx, input.SimKeys)
if err != nil {
return nil, err
}
if len(simKeys) == 0 {
g.Log().Debug(ctx, "SimKeyFill.batchGetSimKey, simKeys is empty, input %+v", input)
return nil, gerror.New("simKeys is empty")
}
input.SimKeys = simKeys
output, err = service.UpstreamAPI().SimBase(ctx, input)
if err != nil {
return nil, err
}
return
}
func (s *sSimKeyFill) batchGetSimKey(ctx context.Context, inputs []*model.SimKey) ([]*model.SimKey, error) {
queryICCIDKey := "sim:code:iccid:"
queryMSISDNKey := "sim:code:msisdn:"
queryIMSIKey := "sim:code:imsi:"
if len(inputs) == 0 {
g.Log().Debugf(ctx, "SimKeyFill.batchGetSimKey, The parameter is empty, inputs %+v", inputs)
return nil, gerror.New("参数不能为空")
}
var redisKeys []string
var dbKeys []string
simKeys := []*model.SimKey{}
// 设置Redis查询Key
for _, in := range inputs {
switch {
case in.ICCID != "":
key := queryICCIDKey + in.ICCID
redisKeys = append(redisKeys, key)
case in.MSISDN != "":
key := queryMSISDNKey + in.MSISDN
redisKeys = append(redisKeys, key)
case in.IMSI != "":
key := queryIMSIKey + in.IMSI
redisKeys = append(redisKeys, key)
default:
g.Log().Debugf(ctx, "SimKeyFill.batchGetSimKey, The parameter is empty, inputs %+v", inputs)
return nil, gerror.New("iccid/msisdn/imsi至少一个不能为空")
}
}
// 查询Redis数据
g.Log().Info(ctx, "SimKeyFill.batchGetSimKey, query redis")
vals, err := g.Redis().MGet(ctx, redisKeys...)
if err != nil {
g.Log().Debug(ctx, "SimKeyFill.batchGetSimKey, query redis error %+v, redisKeys %+v, inputs %+v", err, redisKeys, inputs)
} else {
// 解析Redis数据
for _, key := range redisKeys {
simKey := new(model.SimKey)
err := vals[key].Struct(&simKey)
if err != nil {
g.Log().Debug(ctx, "SimKeyFill.batchGetSimKey, unmarshalValue error %+v, key %s, inputs %+v", err, key, inputs)
dbKeys = append(dbKeys, key)
continue
}
simKeys = append(simKeys, simKey)
}
}
g.Log().Info(ctx, "SimKeyFill.batchGetSimKey, query db")
if len(dbKeys) > 0 {
// 解析数据库查询Key
var dbIccid []string
var dbMsisdn []string
var dbImsi []string
for _, key := range dbKeys {
keySplit := strings.Split(key, ":")
if len(keySplit) != 4 {
g.Log().Debug(ctx, "SimKeyFill.batchGetSimKey, Error in splicing data, key %s, keySplit %v", key, keySplit)
continue
}
switch keySplit[2] {
case "iccid":
dbIccid = append(dbIccid, keySplit[3])
case "msisdn":
dbMsisdn = append(dbMsisdn, keySplit[3])
case "imsi":
dbImsi = append(dbImsi, keySplit[3])
}
}
mod := dao.TSimCardInfo.Ctx(ctx)
if len(dbIccid) > 0 {
mod = mod.WhereOrIn(dao.TSimCardInfo.Columns().Iccid, dbIccid)
}
if len(dbMsisdn) > 0 {
mod = mod.WhereOrIn(dao.TSimCardInfo.Columns().Msisdn, dbMsisdn)
}
if len(dbImsi) > 0 {
mod = mod.WhereOrIn(dao.TSimCardInfo.Columns().Imsi, dbImsi)
}
// 查询数据库数据
var simInfos []*entity.TSimCardInfo
err = mod.Scan(&simInfos)
if err != nil {
g.Log().Error(ctx, "SimKeyFill.batchGetSimKey, query db error %+v, dbKeys %+v, inputs %+v", err, dbKeys, inputs)
return nil, gerror.New("查询数据库数据失败")
}
if len(simInfos) == 0 {
g.Log().Debug(ctx, "SimKeyFill.batchGetSimKey, simInfos is empty, dbKeys %+v, inputs %+v", dbKeys, inputs)
return nil, gerror.New("卡信息不存在")
}
// 整理卡信息
simInfoMap := make(map[string]*model.SimKey)
for _, SimInfo := range simInfos {
simInfoMap[queryICCIDKey+SimInfo.Iccid] = &model.SimKey{
ICCID: SimInfo.Iccid,
MSISDN: SimInfo.Msisdn,
IMSI: SimInfo.Imsi,
}
simInfoMap[queryMSISDNKey+SimInfo.Msisdn] = &model.SimKey{
MSISDN: SimInfo.Msisdn,
IMSI: SimInfo.Imsi,
ICCID: SimInfo.Iccid,
}
simInfoMap[queryIMSIKey+SimInfo.Imsi] = &model.SimKey{
IMSI: SimInfo.Imsi,
ICCID: SimInfo.Iccid,
MSISDN: SimInfo.Msisdn,
}
}
// 新建一个线程,进行Redis数据存储
go s.SetRedis(ctx, simInfoMap)
// 解析数据库数据
for _, info := range dbKeys {
simKeys = append(simKeys, simInfoMap[info])
}
}
return simKeys, nil
}
func (s *sSimKeyFill) SetRedis(ctx context.Context, simInfoMap map[string]*model.SimKey) {
for key, simKey := range simInfoMap {
_, err := g.Redis().Set(ctx, key, *simKey)
if err != nil {
g.Log().Debugf(ctx, "SimKeyFill.batchGetSimKey, Set error %+v, key %s, simKey %+v", err, key, simKey)
continue
}
}
}
@@ -0,0 +1,61 @@
package upstreamapi
import (
"context"
"encoding/json"
"strings"
"testing"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
)
type errCodeTestChannel struct {
response model.SimBaseResJson
}
func (f *errCodeTestChannel) SimBase(context.Context, *model.SimBaseReqJson) (model.SimBaseResJson, error) {
return f.response, nil
}
func TestSimBasePreservesCarrierErrCode(t *testing.T) {
const payload = `{"code":0,"message":"success","data":[{"code":9999,"message":"http status 797","errCode":"S00004","simKey":{"chanKey":"channel_003","iccid":"8986112520409968013"},"resData":{}}]}`
var upstream model.SimResponseData[model.SimBaseResJson]
if err := json.Unmarshal([]byte(payload), &upstream); err != nil {
t.Fatal(err)
}
if len(upstream.Data) != 1 {
t.Fatalf("decoded results = %d, want 1", len(upstream.Data))
}
if upstream.Data[0].ErrCode != "S00004" {
t.Fatalf("decoded errCode = %q, want S00004", upstream.Data[0].ErrCode)
}
api := NewUpstreamAPI()
api.RegisterChan("channel_003", "test", &model.SimCardChannelCache{
Instance: &errCodeTestChannel{response: upstream.Data},
})
output, err := api.SimBase(context.Background(), &model.SimBaseReqJson{
SimKeys: []*model.SimKey{{
ChanKey: "channel_003",
ICCID: "8986112520409968013",
}},
})
if err != nil {
t.Fatal(err)
}
if len(output) != 1 {
t.Fatalf("proxied results = %d, want 1", len(output))
}
if output[0].ErrCode != "S00004" {
t.Fatalf("proxied errCode = %q, want S00004", output[0].ErrCode)
}
encoded, err := json.Marshal(output)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(encoded), `"errCode":"S00004"`) {
t.Fatalf("serialized response dropped errCode: %s", encoded)
}
}
@@ -0,0 +1,171 @@
package upstreamapi
import (
"context"
"path/filepath"
"testing"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/channelconfig"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
type normalGateTestChannel struct {
model.SimCardChannelI
}
type normalGateTestTemplate struct {
calls int
lastConfig *model.SimChannelConfigItemCache
}
func (f *normalGateTestTemplate) NewTemp(_ context.Context, cfg *model.SimChannelConfigItemCache) model.SimCardChannelI {
f.calls++
f.lastConfig = cfg
return &normalGateTestChannel{}
}
// Exercise real ORM decoding and config versioning as well as both creation paths.
// All SQL runs against a temporary SQLite file, never a configured environment DB.
func TestUnregisterChanIsIdempotent(t *testing.T) {
api := NewUpstreamAPI()
api.RegisterChan("missing", "v1", &model.SimCardChannelCache{})
api.UnregisterChan("missing")
api.UnregisterChan("missing")
if _, ok := api.GetChan("missing"); ok {
t.Fatal("channel still registered after repeated unregister")
}
}
func TestChannelNormalCreationGate(t *testing.T) {
ctx := context.Background()
if err := gdb.SetConfigGroup("default", gdb.ConfigGroup{{
Type: "sqlite", Name: filepath.Join(t.TempDir(), "channels.db"),
}}); err != nil {
t.Fatal(err)
}
db := g.DB()
t.Cleanup(func() { _ = db.Close(ctx) })
execSQL := func(query string, args ...any) {
t.Helper()
if _, err := db.Exec(ctx, query, args...); err != nil {
t.Fatal(err)
}
}
execSQL("CREATE TABLE t_sim_channels (id INTEGER PRIMARY KEY, chan_key TEXT, temp_key TEXT, chan_name TEXT, is_normal INTEGER NULL)")
execSQL("CREATE TABLE t_sim_channel_template (id INTEGER PRIMARY KEY, temp_key TEXT, temp_model TEXT)")
execSQL("INSERT INTO t_sim_channel_template (id, temp_key, temp_model) VALUES (1, 'gate_test', 'direct')")
oldConfig := service.ChannelConfig()
t.Cleanup(func() { service.RegisterChannelConfig(oldConfig) })
for _, path := range []string{"loop", "refresh"} {
t.Run(path, func(t *testing.T) {
for _, tc := range []struct {
name string
value any
eligible bool
}{
{"yes_is_zero", 0, true},
{"no_is_one", 1, false},
{"null", nil, false},
{"unexpected", 2, false},
} {
t.Run(tc.name, func(t *testing.T) {
execSQL("DELETE FROM t_sim_channels")
execSQL("INSERT INTO t_sim_channels (id, chan_key, temp_key, is_normal) VALUES (1, 'gate_channel', 'gate_test', ?)", tc.value)
config := channelconfig.NewChannelConfig()
service.RegisterChannelConfig(config)
api := NewUpstreamAPI()
factory := &normalGateTestTemplate{}
api.RegisterTemp("gate_test", factory)
reload := func() {
t.Helper()
var err error
if path == "loop" {
err = config.LoadAllChannelConfig(ctx)
if err == nil {
err = api.loop(ctx)
}
} else {
err = api.RefreshChannel(ctx, "gate_channel")
}
if err != nil {
t.Fatalf("refresh must succeed: %v", err)
}
}
reload()
cfg, ok := config.GetChannel("gate_channel")
if !ok || cfg.CanCreateInstance() != tc.eligible {
t.Fatalf("wrong eligibility after ORM scan: %+v", cfg)
}
if tc.value == nil && cfg.IsNormal != nil {
t.Fatalf("SQL NULL decoded as non-nil: %v", *cfg.IsNormal)
}
if cfg.Channel.ChanKey != "gate_channel" {
t.Fatal("embedded channel fields were not scanned")
}
_, exists := api.GetChan("gate_channel")
if exists != tc.eligible || (factory.calls == 1) != tc.eligible {
t.Fatalf("instance exists=%v factory calls=%d eligible=%v", exists, factory.calls, tc.eligible)
}
initialVersion := cfg.Version
// Only is_normal changes: the configuration cache must notice it.
execSQL("UPDATE t_sim_channels SET is_normal=0")
reload()
current, ok := api.GetChan("gate_channel")
if !ok || current.Instance == nil {
t.Fatal("eligible channel was not instantiated")
}
cfg, _ = config.GetChannel("gate_channel")
if !tc.eligible && cfg.Version == initialVersion {
t.Fatal("changing only is_normal did not change config version")
}
oldCalls := factory.calls
// Changing only is_normal must evict the old runtime instance.
execSQL("UPDATE t_sim_channels SET is_normal=1")
reload()
if _, exists := api.GetChan("gate_channel"); exists || factory.calls != oldCalls {
t.Fatal("ineligible channel retained or recreated its runtime instance")
}
cfg, _ = config.GetChannel("gate_channel")
if cfg.CanCreateInstance() || cfg.Version == current.Version {
t.Fatal("ineligible configuration was not cached with a new version")
}
// Disabled refreshes remain successful even if the template disappears.
execSQL("UPDATE t_sim_channels SET chan_name='changed while disabled', temp_key='missing_template'")
reload()
reload()
if _, exists := api.GetChan("gate_channel"); exists || factory.calls != oldCalls {
t.Fatal("repeated ineligible refresh was not an idempotent removal")
}
// A process restart cannot recreate the disabled instance.
restarted := NewUpstreamAPI()
restarted.RegisterTemp("gate_test", factory)
if err := restarted.loop(ctx); err != nil {
t.Fatal(err)
}
if _, ok := restarted.GetChan("gate_channel"); ok {
t.Fatal("reload recreated disabled instance")
}
execSQL("UPDATE t_sim_channels SET is_normal=0, temp_key='gate_test'")
reload()
restored, exists := api.GetChan("gate_channel")
if !exists || restored == current || factory.calls != oldCalls+1 ||
factory.lastConfig.Channel.ChanName != "changed while disabled" {
t.Fatal("restored channel did not rebuild using the latest configuration")
}
})
}
})
}
}
+388
View File
@@ -0,0 +1,388 @@
package upstreamapi
import (
"context"
"encoding/json"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"strings"
"sync"
"time"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
)
var REDIS_KEY = "sim:chan:iccid:"
type sUpstreamAPI struct {
tempMap map[string]model.SimCardTempI
chanMap map[string]*model.SimCardChannelCache
channelMux sync.Mutex
channelOps map[string]*sync.Mutex
sync.RWMutex
running bool
}
func NewUpstreamAPI() *sUpstreamAPI {
return &sUpstreamAPI{
chanMap: make(map[string]*model.SimCardChannelCache),
tempMap: make(map[string]model.SimCardTempI),
channelOps: make(map[string]*sync.Mutex),
}
}
func init() {
service.RegisterUpstreamAPI(NewUpstreamAPI())
}
func (u *sUpstreamAPI) Start(ctx context.Context) error {
u.running = true
lastReloadTime := int64(0)
go func() {
for u.running {
now := time.Now().Unix()
reloadInterval := g.Cfg().MustGet(ctx, "upstreamapi.reload_interval", 60).Int64()
if now-lastReloadTime >= reloadInterval {
lastReloadTime = now
err := u.loop(ctx)
if err != nil {
g.Log().Errorf(ctx, "sUpstreamAPI loop err %+v", err)
}
}
time.Sleep(1 * time.Second)
}
}()
g.Log().Infof(ctx, "sUpstreamAPI Start success")
return nil
}
func (u *sUpstreamAPI) Stop(ctx context.Context) error {
u.running = false
return nil
}
func (u *sUpstreamAPI) RefreshChannel(ctx context.Context, chanKey string) error {
unlock := u.lockChannel(chanKey)
defer unlock()
return u.refreshChannelLocked(ctx, chanKey)
}
func (u *sUpstreamAPI) refreshChannelLocked(ctx context.Context, chanKey string) error {
if err := service.ChannelConfig().RefreshChannel(ctx, chanKey); err != nil {
return err
}
channelConf, ok := service.ChannelConfig().GetChannel(chanKey)
if !ok || channelConf == nil {
return gerror.Newf("channel config not found: %s", chanKey)
}
if !channelConf.CanCreateInstance() {
u.UnregisterChan(chanKey)
g.Log().Infof(ctx, "channel instance removed because is_normal is not 0, chanKey: %s", chanKey)
return nil
}
temp, ok := u.GetTemp(channelConf.Template.TempKey)
if !ok || temp == nil {
return gerror.Newf("channel template not registered: %s", channelConf.Template.TempKey)
}
instance := temp.NewTemp(ctx, channelConf)
if instance == nil {
return gerror.Newf("create channel instance failed: %s", chanKey)
}
u.RegisterChan(chanKey, channelConf.Version, &model.SimCardChannelCache{
Instance: instance,
Version: channelConf.Version,
})
g.Log().Infof(ctx, "channel refreshed, chanKey: %s, version: %s", chanKey, channelConf.Version)
return nil
}
// RemoveChannel drops the cached configuration and runtime instance for a
// channel. Deleting an already absent key is a successful no-op.
func (u *sUpstreamAPI) RemoveChannel(ctx context.Context, chanKey string) error {
chanKey = strings.TrimSpace(chanKey)
if chanKey == "" {
return gerror.New("chanKey is required")
}
unlock := u.lockChannel(chanKey)
defer unlock()
if err := service.ChannelConfig().RemoveChannel(ctx, chanKey); err != nil {
return err
}
u.UnregisterChan(chanKey)
g.Log().Infof(ctx, "channel removed, chanKey: %s", chanKey)
return nil
}
func (u *sUpstreamAPI) loop(ctx context.Context) error {
channelConfigs, err := service.ChannelConfig().GetAllChannel(ctx)
if err != nil {
return err
}
for _, snapshot := range channelConfigs {
if snapshot == nil || snapshot.Channel == nil {
continue
}
chanKey := snapshot.Channel.ChanKey
func() {
unlock := u.lockChannel(chanKey)
defer unlock()
if err := service.ChannelConfig().RefreshChannel(ctx, chanKey); err != nil {
g.Log().Errorf(ctx, "reload channel config failed, chanKey: %s, err: %+v", chanKey, err)
return
}
channelConf, ok := service.ChannelConfig().GetChannel(chanKey)
if !ok || channelConf == nil {
return
}
if !channelConf.CanCreateInstance() {
u.UnregisterChan(chanKey)
return
}
tempKey := channelConf.Template.TempKey
channelCache, ok := u.GetChan(chanKey)
if ok && channelCache != nil && channelCache.Version == channelConf.Version {
return
}
isNew := !ok || channelCache == nil
g.Log().Infof(ctx, "Create channel instance begin, chanKey: %s, isNew: %v", chanKey, isNew)
temp, ok := u.GetTemp(tempKey)
if !ok || temp == nil {
g.Log().Errorf(ctx, "Create channel instance failed, Temp not found, tempKey: %s", tempKey)
return
}
instance := temp.NewTemp(ctx, channelConf)
if instance == nil {
g.Log().Errorf(ctx, "Create channel instance failed, Temp new instance failed, tempKey: %s", tempKey)
return
}
channelCache = &model.SimCardChannelCache{Instance: instance, Version: channelConf.Version}
u.RegisterChan(chanKey, channelConf.Version, channelCache)
g.Log().Infof(ctx, "Create channel instance success, chanKey: %s, version: %s", chanKey, channelConf.Version)
}()
}
return nil
}
func (u *sUpstreamAPI) lockChannel(chanKey string) func() {
u.channelMux.Lock()
lock := u.channelOps[chanKey]
if lock == nil {
lock = &sync.Mutex{}
u.channelOps[chanKey] = lock
}
u.channelMux.Unlock()
lock.Lock()
return lock.Unlock
}
func (u *sUpstreamAPI) UnregisterChan(chanKey string) {
u.Lock()
delete(u.chanMap, chanKey)
u.Unlock()
}
func (u *sUpstreamAPI) RegisterChan(chanKey string, version string, channel *model.SimCardChannelCache) {
u.Lock()
defer u.Unlock()
u.chanMap[chanKey] = channel
}
func (u *sUpstreamAPI) removeChan(chanKey string) {
u.Lock()
defer u.Unlock()
delete(u.chanMap, chanKey)
}
func (u *sUpstreamAPI) RegisterTemp(tempKey string, channel model.SimCardTempI) {
u.Lock()
defer u.Unlock()
u.tempMap[tempKey] = channel
}
func (u *sUpstreamAPI) GetTemp(tempKey string) (model.SimCardTempI, bool) {
u.RLock()
defer u.RUnlock()
temp, ok := u.tempMap[tempKey]
return temp, ok
}
func (u *sUpstreamAPI) GetChan(chanKey string) (*model.SimCardChannelCache, bool) {
u.RLock()
defer u.RUnlock()
channel, ok := u.chanMap[chanKey]
return channel, ok
}
func (u *sUpstreamAPI) packChanKey(ctx context.Context, simKeys []*model.SimKey) (map[string][]*model.SimKey, error) {
chanKeyMap := make(map[string][]*model.SimKey)
simKeyMap := make(map[string]*model.SimKey, len(simKeys))
unknownChanKeyIccids := make([]string, 0)
queryRedisIccidKeys := make([]string, 0)
queryDbIccids := make([]string, 0)
// 检查通道id是都携带
for _, simKey := range simKeys {
simKeyMap[simKey.ICCID] = simKey
if simKey.ChanKey == "" {
unknownChanKeyIccids = append(unknownChanKeyIccids, simKey.ICCID)
queryRedisIccidKeys = append(queryRedisIccidKeys, REDIS_KEY+simKey.ICCID)
} else {
chanKeyMap[simKey.ChanKey] = append(chanKeyMap[simKey.ChanKey], simKey)
}
}
if len(unknownChanKeyIccids) == 0 {
return chanKeyMap, nil
}
allIccid := make(map[string]bool)
for _, iccid := range unknownChanKeyIccids {
allIccid[iccid] = false
}
// TODO: 临时数据库查询,后面添加内存缓存和Redis缓存
// 从Redis中查询通道id
redisChanKeys, err := g.Redis().MGet(ctx, queryRedisIccidKeys...)
if err != nil {
g.Log().Error(ctx, "sUpstreamAPI.packChanKey, query redis error %+v, queryRedisIccidKeys %+v", err, queryRedisIccidKeys)
return nil, gerror.New("查询Redis通道id失败")
}
for iccidKey, chanKey := range redisChanKeys {
iccidSplit := strings.Split(iccidKey, ":")
if len(iccidSplit) != 4 {
g.Log().Debug(ctx, "sUpstreamAPI.packChanKey, iccidKey format error, iccidKey:%s", iccidKey)
continue
}
iccid := iccidSplit[3]
chanKeyStr := chanKey.String()
if chanKeyStr == "" {
// 查询redis为空
g.Log().Debug(ctx, "sUpstreamAPI.packChanKey, query redis empty, iccid:%s, simKeys:%v", iccid, simKeys)
queryDbIccids = append(queryDbIccids, iccid)
continue
}
allIccid[iccid] = true
simKey := simKeyMap[iccid]
if simKey == nil {
g.Log().Debug(ctx, "sUpstreamAPI.packChanKey, simKey not found, iccid:%s, simKeys:%v", iccid, simKeys)
continue
}
chanKeyMap[chanKeyStr] = append(chanKeyMap[chanKeyStr], &model.SimKey{
ChanKey: chanKeyStr,
ICCID: iccid,
IMSI: simKey.IMSI,
MSISDN: simKey.MSISDN,
})
}
if len(queryDbIccids) == 0 {
return chanKeyMap, nil
}
var simCardInfos []*entity.TSimCardInfo
g.Log().Info(ctx, "sUpstreamAPI.packChanKey, query db, %v", queryDbIccids)
err = dao.TSimCardInfo.Ctx(ctx).
Fields(dao.TSimCardInfo.Columns().ChanKey, dao.TSimCardInfo.Columns().Iccid).
WhereIn(dao.TSimCardInfo.Columns().Iccid, queryDbIccids).Scan(&simCardInfos)
if err != nil {
g.Log().Error(ctx, "sUpstreamAPI.packChanKey, query db error %+v, queryDbIccids %+v, simKeys %+v", err, queryDbIccids, simKeys)
return nil, gerror.New("查询数据库数据失败")
}
for _, simCardInfo := range simCardInfos {
allIccid[simCardInfo.Iccid] = true
simKey, _ := simKeyMap[simCardInfo.Iccid]
chanKeyMap[simCardInfo.ChanKey] = append(chanKeyMap[simCardInfo.ChanKey], &model.SimKey{
ChanKey: simCardInfo.ChanKey,
ICCID: simCardInfo.Iccid,
IMSI: simKey.IMSI,
MSISDN: simKey.MSISDN,
})
}
// 异步写入Redis
go u.SetRedis(ctx, simCardInfos)
for iccid, found := range allIccid {
if !found {
chanKeyMap[""] = append(chanKeyMap[""], &model.SimKey{
ChanKey: "",
ICCID: iccid,
})
}
}
return chanKeyMap, nil
}
// 物联网卡
func (u *sUpstreamAPI) SimBase(ctx context.Context, input *model.SimBaseReqJson) (model.SimBaseResJson, error) {
output := make([]*model.SimBaseRes[json.RawMessage], 0)
inputMap := make(map[string]*model.SimBaseReqJson)
chanKeyMap, err := u.packChanKey(ctx, input.SimKeys)
if err != nil {
return nil, err
}
for chanKey, simKeys := range chanKeyMap {
inputMap[chanKey] = &model.SimBaseReqJson{
SimKeys: simKeys,
ReqData: input.ReqData,
}
}
for chanKey, inputNew := range inputMap {
if chanKey == "" {
for _, simKey := range inputNew.SimKeys {
output = append(output, &model.SimBaseRes[json.RawMessage]{
SimKey: simKey,
Code: consts.ApiStatusError,
Message: "通道未配置",
ResData: json.RawMessage(`{"status": "-2"}`),
})
}
continue
}
channel, ok := u.GetChan(chanKey)
if !ok || channel.Instance == nil {
for _, simKey := range inputNew.SimKeys {
output = append(output, &model.SimBaseRes[json.RawMessage]{
SimKey: simKey,
Code: consts.ApiStatusError,
Message: "通道未实例化",
ResData: json.RawMessage(`{"status": "-2"}`),
})
}
continue
}
out, err := channel.Instance.SimBase(ctx, inputNew)
if err != nil {
for _, simKey := range inputNew.SimKeys {
output = append(output, &model.SimBaseRes[json.RawMessage]{
SimKey: simKey,
Code: consts.ApiStatusError,
Message: err.Error(),
ResData: json.RawMessage(`{"status": "-2"}`),
})
}
} else {
for _, item := range out {
output = append(output, &model.SimBaseRes[json.RawMessage]{
SimKey: item.SimKey,
Code: item.Code,
Message: item.Message,
ErrCode: item.ErrCode,
ResData: item.ResData,
})
}
}
}
return output, nil
}
// 异步写入redis
func (u *sUpstreamAPI) SetRedis(ctx context.Context, simCardInfos []*entity.TSimCardInfo) {
for _, simCardInfo := range simCardInfos {
_, err := g.Redis().Set(ctx, REDIS_KEY+simCardInfo.Iccid, simCardInfo.ChanKey)
if err != nil {
g.Log().Debugf(ctx, "sUpstreamAPI.SetRedis, Set Redis error %+v, key %s, simCardInfo %+v", err, REDIS_KEY+simCardInfo.Iccid, simCardInfo)
continue
}
}
}
@@ -0,0 +1,24 @@
package upstreamimpl
import (
"context"
"testing"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
)
func TestApikaProxyTemplateRegistration(t *testing.T) {
template, ok := service.UpstreamAPI().GetTemp("sim_onelink_kedang_v1")
if !ok || template == nil {
t.Fatal("apika proxy template not registered")
}
config := &model.SimChannelConfigItemCache{
Template: &entity.TSimChannelTemplate{TempKey: "sim_onelink_kedang_v1"},
Channel: &entity.TSimChannels{ChanKey: "apika-test"},
}
if instance := template.NewTemp(context.Background(), config); instance == nil {
t.Fatal("apika proxy template failed to instantiate")
}
}
+96
View File
@@ -0,0 +1,96 @@
package dx
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
// Config 网关鉴权与请求配置
type Config struct {
BaseURL string // 默认 https://cmp-api.ctwing.cn:20164
AppKey string
AppSecret string
Timeout time.Duration
}
// Client API 客户端
type Client struct {
baseURL string
appKey string
appSecret string
http *http.Client
}
// New 创建客户端
func NewClient(cfg Config) *Client {
base := cfg.BaseURL
if base == "" {
base = DefaultBaseURL
}
timeout := cfg.Timeout
if timeout == 0 {
timeout = 10 * time.Second
}
return &Client{
baseURL: strings.TrimSuffix(base, "/"),
appKey: cfg.AppKey,
appSecret: cfg.AppSecret,
http: &http.Client{
Timeout: timeout,
},
}
}
// DoGetRequest 通用GET请求(自动处理签名)
// endpoint: API路径,如 "/api/v1/openbill/queryTraffic"
// params: 查询参数,需调用方自行构建
func (c *Client) DoGetRequest(ctx context.Context, endpoint string, params url.Values) ([]byte, error) {
req, err := c.doGetRequest(ctx, endpoint, params)
if err != nil {
return nil, err
}
return c.doRequest(req)
}
// doGetRequest 封装通用的 GET 请求构建逻辑
func (c *Client) doGetRequest(ctx context.Context, endpoint string, params url.Values) (*http.Request, error) {
queryString := params.Encode()
timestamp := GetTimestampGMT()
signStr, err := BuildSign(queryString, "", c.appSecret, timestamp)
if err != nil {
return nil, err
}
reqURL := c.baseURL + endpoint + "?" + queryString
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
if err != nil {
return nil, err
}
req.Header.Set("AppKey", c.appKey)
req.Header.Set("Sign", signStr)
req.Header.Set("Timestamp", timestamp)
return req, nil
}
// doRequest 执行HTTP请求
func (c *Client) doRequest(req *http.Request) ([]byte, error) {
resp, err := c.http.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("http status %d: %s", resp.StatusCode, string(body))
}
return body, nil
}
+96
View File
@@ -0,0 +1,96 @@
package dx
import (
"context"
"crypto/md5"
"encoding/hex"
"sort"
"strings"
"time"
"github.com/gogf/gf/v2/frame/g"
)
const (
// DefaultBaseURL 电信API基础地址
DefaultBaseURL = "https://cmp-api.ctwing.cn:20164"
)
// GetTimestampGMT 返回 GMT 时区的 yyyyMMddHHmmss 格式时间戳
func GetTimestampGMT() string {
return time.Now().UTC().Format("20060102150405")
}
// OrderParams 将 URL params 按 key 字典序排序
// 输入如 "b=1&c=2&a=1",输出 "a=1&b=1&c=2"
func OrderParams(params string) (string, error) {
if params == "" {
return "", nil
}
kvs := strings.Split(params, "&")
m := make(map[string]string, len(kvs))
for _, kv := range kvs {
idx := strings.Index(kv, "=")
if idx < 0 {
continue
}
m[kv[:idx]] = kv[idx+1:]
}
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
var parts []string
for _, k := range keys {
parts = append(parts, k+"="+m[k])
}
return strings.Join(parts, "&"), nil
}
// SignMD5 对 reqStr 做 MD5 加密,返回小写十六进制字符串
func SignMD5(reqStr string) string {
h := md5.Sum([]byte(reqStr))
return strings.ToLower(hex.EncodeToString(h[:]))
}
// BuildSign 构造签名:params 排序后拼接 params+body+secretKey+timestamp 再 MD5
// 注意:与参考项目一致,对 MD5 结果再做一次 hex 编码
func BuildSign(params, body, secretKey, timestamp string) (string, error) {
ordered, err := OrderParams(params)
if err != nil {
return "", err
}
reqStr := ordered + body + secretKey + timestamp
md5Str := SignMD5(reqStr)
// 对 MD5 结果再做一次 hex 编码(与参考项目一致)
hexStr := hex.EncodeToString([]byte(md5Str))
return hexStr, nil
}
// BuildSignWithDebug 构造签名并输出调试信息
func BuildSignWithDebug(ctx context.Context, params, body, secretKey, timestamp, appKey string) (string, error) {
ordered, err := OrderParams(params)
if err != nil {
return "", err
}
reqStr := ordered + body + secretKey + timestamp
maskedSecret := secretKey
g.Log().Debugf(ctx, "[签名调试] AppKey: %s", appKey)
g.Log().Debugf(ctx, "[签名调试] SecretKey: %s", maskedSecret)
g.Log().Debugf(ctx, "[签名调试] Timestamp: %s", timestamp)
g.Log().Debugf(ctx, "[签名调试] 排序后参数: %s", ordered)
g.Log().Debugf(ctx, "[签名调试] Body: '%s'", body)
g.Log().Debugf(ctx, "[签名调试] 签名原串: %s%s[SecretKey]%s", ordered, body, timestamp)
md5Str := SignMD5(reqStr)
g.Log().Debugf(ctx, "[签名调试] MD5结果: %s", md5Str)
// 对 MD5 结果再做一次 hex 编码
hexStr := hex.EncodeToString([]byte(md5Str))
g.Log().Debugf(ctx, "[签名调试] 最终签名(hex编码后): %s", hexStr)
return hexStr, nil
}
@@ -0,0 +1,32 @@
package upstreamimpl
import (
"encoding/json"
"fmt"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/net/gclient"
)
func readGatewayResponse(response *gclient.Response) (model.SimBaseResJson, error) {
defer response.Close()
body := response.ReadAll()
var output struct {
Code *int `json:"code"`
Message string `json:"message"`
Data model.SimBaseResJson `json:"data"`
}
if err := json.Unmarshal(body, &output); err != nil {
return nil, gerror.Wrap(err, "解析网关响应失败")
}
if response.StatusCode < 200 || response.StatusCode >= 300 || output.Code == nil || *output.Code != 0 {
message := strings.TrimSpace(output.Message)
if message == "" {
message = fmt.Sprintf("网关响应无效,HTTP状态码:%d", response.StatusCode)
}
return nil, gerror.New(message)
}
return output.Data, nil
}
@@ -0,0 +1,77 @@
package upstreamimpl_test
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcfg"
)
func TestMingqianProxyTemplate(t *testing.T) {
template, ok := service.UpstreamAPI().GetTemp("sim_mingqian_v3")
if !ok || template == nil {
t.Fatal("mingqian proxy template missing")
}
instance := template.NewTemp(context.Background(), &model.SimChannelConfigItemCache{Template: &entity.TSimChannelTemplate{TempKey: "sim_mingqian_v3"}, Channel: &entity.TSimChannels{ChanKey: "mingqian-test"}})
if instance == nil {
t.Fatal("mingqian proxy instance missing")
}
}
func TestMingqianProxyPreservesFlowFields(t *testing.T) {
const response = `{"code":0,"data":[{"code":0,"message":"","simKey":{"chanKey":"mingqian-test","iccid":"A"},"resData":{"dataUsage":1,"packageFlow":{"totalFlow":10240,"dataUsage":8192,"remainingFlow":2048}}},{"code":0,"message":"package flow unavailable","simKey":{"chanKey":"mingqian-test","iccid":"B"},"resData":{"dataUsage":2,"packageFlowError":"mingqian: business code 500"}},{"code":1,"message":"monthly usage unavailable","simKey":{"chanKey":"mingqian-test","iccid":"C"},"resData":{"dataUsage":0,"packageFlow":{"totalFlow":10,"dataUsage":8,"remainingFlow":2}}}]}`
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
if request.Method != http.MethodPost || request.URL.Path != "/sim/query/sim-datausage" {
t.Errorf("wrong gateway route: %s %s", request.Method, request.URL.Path)
}
var input model.SimBaseReqJson
if err := request.ParseForm(); err != nil {
t.Error(err)
}
if err := json.Unmarshal([]byte(request.Form.Get("simKeys")), &input.SimKeys); err != nil || len(input.SimKeys) != 3 || input.SimKeys[0].ICCID != "A" {
t.Errorf("request changed: %+v %v", input, err)
}
writer.Header().Set("Content-Type", "application/json")
fmt.Fprint(writer, response)
}))
t.Cleanup(server.Close)
config, err := gcfg.NewAdapterContent(fmt.Sprintf(`{"gateway":{"address":%q,"timeout":"1s"}}`, strings.TrimPrefix(server.URL, "http://")))
if err != nil {
t.Fatal(err)
}
original := g.Cfg().GetAdapter()
g.Cfg().SetAdapter(config)
t.Cleanup(func() { g.Cfg().SetAdapter(original) })
template, ok := service.UpstreamAPI().GetTemp("sim_mingqian_v3")
if !ok {
t.Fatal("missing Mingqian registration")
}
instance := template.NewTemp(context.Background(), &model.SimChannelConfigItemCache{Template: &entity.TSimChannelTemplate{TempKey: "sim_mingqian_v3"}, Channel: &entity.TSimChannels{ChanKey: "mingqian-test"}})
request := &ghttp.Request{Request: httptest.NewRequest(http.MethodPost, "/sim/query/sim-datausage", nil)}
output, err := instance.SimBase(request.Context(), &model.SimBaseReqJson{
SimKeys: []*model.SimKey{{ChanKey: "mingqian-test", ICCID: "A"}, {ChanKey: "mingqian-test", ICCID: "B"}, {ChanKey: "mingqian-test", ICCID: "C"}},
ReqData: json.RawMessage(`{}`),
})
if err != nil {
t.Fatal(err)
}
var expected model.SimResponseData[model.SimBaseResJson]
if err := json.Unmarshal([]byte(response), &expected); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(output, expected.Data) {
t.Fatalf("proxy dropped package fields or changed failure status: %+v", output)
}
}
@@ -0,0 +1,33 @@
package upstreamimpl_test
import (
"context"
"testing"
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
)
func TestMobilePublicProxyTemplateRegistration(t *testing.T) {
for _, templateKey := range []string{
"sim_onelink_daliuliang_v1",
"sim__daliuliang_v1",
"sim_onelink_mobile_public_v1",
} {
t.Run(templateKey, func(t *testing.T) {
template, ok := service.UpstreamAPI().GetTemp(templateKey)
if !ok || template == nil {
t.Fatal("mobile public proxy template not registered")
}
config := &model.SimChannelConfigItemCache{
Template: &entity.TSimChannelTemplate{TempKey: templateKey},
Channel: &entity.TSimChannels{ChanKey: "daliuliang-test"},
}
if instance := template.NewTemp(context.Background(), config); instance == nil {
t.Fatal("mobile public proxy failed to instantiate")
}
})
}
}
+95
View File
@@ -0,0 +1,95 @@
package upstreamimpl
import (
"context"
"fmt"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/upstreamimpl/dx"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"regexp"
"time"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gsvc"
)
// XML命名空间正则
var xmlNamespaceRegex = regexp.MustCompile(`xmlns[^=]*="[^"]*"\s*|:web:`)
type sSimDX struct {
channelConfig *model.SimChannelConfigItemCache
client *dx.Client
}
func NewSimDX() *sSimDX {
return &sSimDX{}
}
func init() {
s := NewSimDX()
service.UpstreamAPI().RegisterTemp("sim_dx_v1", s)
service.UpstreamAPI().RegisterTemp("sim_dx_v3", s)
service.RegisterSimDX(s)
}
func (s *sSimDX) NewTemp(ctx context.Context, channelConfig *model.SimChannelConfigItemCache) model.SimCardChannelI {
client := dx.NewClient(dx.Config{
BaseURL: dx.DefaultBaseURL,
AppKey: channelConfig.Channel.AppKey,
AppSecret: channelConfig.Channel.AppSecret,
})
return &sSimDX{
channelConfig: channelConfig,
client: client,
}
}
func (s *sSimDX) getUpstreamUrl(ctx context.Context, serviceUrl string) (string, error) {
address, err := g.Cfg().Get(ctx, "gateway.address")
if err != nil {
return "", err
}
return fmt.Sprintf("http://%s%s", address, serviceUrl), nil
// return fmt.Sprintf("http://%s%s", "127.0.0.1:8110", serviceUrl), nil
}
func (s *sSimDX) getUpstreamTimeout(ctx context.Context) time.Duration {
timeout, _ := g.Cfg().Get(ctx, "gateway.timeout")
if timeout == nil {
return 10 * time.Second
}
if d := timeout.Duration(); d > 0 {
return d
}
if seconds := timeout.Int(); seconds > 0 {
return time.Duration(seconds) * time.Second
}
return 10 * time.Second
}
func (s *sSimDX) SimBase(ctx context.Context, input *model.SimBaseReqJson) (model.SimBaseResJson, error) {
r := g.RequestFromCtx(ctx)
if r.Method != "POST" {
return nil, gerror.New("method not allowed")
}
path := r.URL.Path
if path == "" {
return nil, gerror.New("path is required")
}
url, err := s.getUpstreamUrl(ctx, path)
if err != nil {
g.Log().Debugf(ctx, "SimBase getUpstreamUrl error %+v, ", err)
return nil, err
}
client := g.Client()
client.SetDiscovery(gsvc.GetRegistry())
client.SetTimeout(s.getUpstreamTimeout(ctx) * time.Duration(len(input.SimKeys)))
resp, err := client.Post(ctx, url, input)
if err != nil {
g.Log().Errorf(ctx, "SimBase post error %+v,url %s, params %+v, ", err, url, input)
return nil, err
}
return readGatewayResponse(resp)
}
+116
View File
@@ -0,0 +1,116 @@
package upstreamimpl
import (
"context"
"fmt"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
"sync/atomic"
"time"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gsvc"
"golang.org/x/sync/singleflight"
)
const (
gUrlBase = "https://api.iot.10086.cn/v5/"
)
type sSimYD struct {
channelConfig *model.SimChannelConfigItemCache
seqId atomic.Int64
tokenFlight singleflight.Group
}
func NewSimYD() *sSimYD {
return &sSimYD{}
}
func init() {
s := NewSimYD()
service.UpstreamAPI().RegisterTemp("sim_yd_v5", s)
service.UpstreamAPI().RegisterTemp("sim_yd_mock_v5", s)
// These channel implementations live in the upstream gateway. The proxy
// forwards their API calls through the same generic gateway route, so any
// template registered in iotlcm-upstream-chan must be registered here too:
// channel refresh fails with "channel template not registered" otherwise.
// sSimYD has no template-specific logic, so an alias per template suffices.
service.UpstreamAPI().RegisterTemp("sim_sdtywl_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_tengyu_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_xuzhou_mobile_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_lelian_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_qianxun_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_henan_xinjing_v1", s)
service.UpstreamAPI().RegisterTemp("sim_henan_xinjing_direct_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_zibo_v1", s)
service.UpstreamAPI().RegisterTemp("sim_tibiot_v1", s)
service.UpstreamAPI().RegisterTemp("sim_qianxun_v3", s)
service.UpstreamAPI().RegisterTemp("sim_yunda_v1", s)
service.UpstreamAPI().RegisterTemp("sim_mingqian_v3", s)
service.UpstreamAPI().RegisterTemp("sim_daihe_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_daihe_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_daliuliang_v1", s)
service.UpstreamAPI().RegisterTemp("sim__daliuliang_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_mobile_public_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_kedang_v1", s)
service.UpstreamAPI().RegisterTemp("lanshang_8003", s)
service.UpstreamAPI().RegisterTemp("lanshang_8000", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_yunze_v1", s)
// 江苏倍信(jsbxkj)共享 Token。协议实现在 chan 的 beixin_token_provider.go;
// proxy 只负责把业务 API 转发到 gateway,与腾宇/澜数同一形态。
service.UpstreamAPI().RegisterTemp("sim_onelink_beixin_v1", s)
service.UpstreamAPI().RegisterTemp("sim_onelink_lanshu_v1", s)
service.RegisterSimYD(s)
}
func (s *sSimYD) NewTemp(ctx context.Context, channelConfig *model.SimChannelConfigItemCache) model.SimCardChannelI {
return &sSimYD{
channelConfig: channelConfig,
}
}
func (s *sSimYD) getUpstreamUrl(ctx context.Context, serviceUrl string) string {
address, _ := g.Cfg().Get(ctx, "gateway.address")
return fmt.Sprintf("http://%s%s", address, serviceUrl)
// return fmt.Sprintf("http://%s%s", "127.0.0.1:8110", serviceUrl)
}
func (s *sSimYD) getUpstreamTimeout(ctx context.Context) time.Duration {
timeout, _ := g.Cfg().Get(ctx, "gateway.timeout")
if timeout == nil {
return 10 * time.Second
}
if d := timeout.Duration(); d > 0 {
return d
}
if seconds := timeout.Int(); seconds > 0 {
return time.Duration(seconds) * time.Second
}
return 10 * time.Second
}
func (s *sSimYD) SimBase(ctx context.Context, input *model.SimBaseReqJson) (model.SimBaseResJson, error) {
r := g.RequestFromCtx(ctx)
if r.Method != "POST" {
g.Log().Errorf(ctx, "SimYD.SimBase, method not allowed, method %s, input %+v", r.Method, input)
return nil, gerror.New("方法调用错误")
}
path := r.URL.Path
if path == "" {
g.Log().Errorf(ctx, "SimYD.SimBase, path is required, input %+v", input)
return nil, gerror.New("路径不能为空")
}
url := s.getUpstreamUrl(ctx, path)
g.Log().Debugf(ctx, "SimYD.SimBase, url %s", url)
client := g.Client()
client.SetDiscovery(gsvc.GetRegistry())
client.SetTimeout(s.getUpstreamTimeout(ctx) * time.Duration(len(input.SimKeys)))
resp, err := client.Post(ctx, url, input)
if err != nil {
g.Log().Errorf(ctx, "SimYD.SimBase, post error %+v,url %s, params %+v, ", err, url, input)
return nil, gerror.New("请求失败")
}
return readGatewayResponse(resp)
}
@@ -0,0 +1,78 @@
package upstreamimpl
import (
"context"
"testing"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
)
// chanTemplateKeys lists every template iotlcm-upstream-chan registers in its
// own upstreamimpl init() functions. Source of truth:
//
// iotlcm-upstream-chan/internal/logic/upstreamimpl/sim_onelink_shared.go:18-29 (shared token family)
// iotlcm-upstream-chan/internal/logic/upstreamimpl/sim_daihe.go:17
// iotlcm-upstream-chan/internal/logic/upstreamimpl/sim_henan_xinjing_direct.go:18
// iotlcm-upstream-chan/internal/logic/upstreamimpl/sim_qianxun.go:25
// iotlcm-upstream-chan/internal/logic/upstreamimpl/sim_yunda.go:19
// iotlcm-upstream-chan/internal/logic/upstreamimpl/sim_recharge.go:19
// iotlcm-upstream-chan/internal/logic/upstreamimpl/sim_yd.go (sim_yd_v5)
// iotlcm-upstream-chan/internal/logic/upstreamimpl/sim_yd_mock.go:21
// iotlcm-upstream-chan/internal/service/mingqian/adapter.go:16
//
// Keep this list in sync when a template is added on the chan side: a template
// missing here is exactly the failure mode where channel refresh returns
// "channel template not registered".
var chanTemplateKeys = []string{
// 共享 Token 族
"sim_onelink_tengyu_v1",
"sim_onelink_xuzhou_mobile_v1",
"sim_onelink_lelian_v1",
"sim_onelink_qianxun_v1",
"sim_onelink_henan_xinjing_v1",
"sim_onelink_daihe_v1",
"sim_onelink_daliuliang_v1",
"sim__daliuliang_v1",
"sim_onelink_mobile_public_v1",
"sim_onelink_kedang_v1",
"sim_onelink_yunze_v1",
"sim_onelink_lanshu_v1",
"sim_onelink_beixin_v1",
// 非共享 Token 族
"sim_yd_v5",
"sim_yd_mock_v5",
"sim_sdtywl_v1",
"sim_daihe_v1",
"sim_henan_xinjing_direct_v1",
"sim_qianxun_v3",
"sim_yunda_v1",
"sim_mingqian_v3",
"sim_tibiot_v1",
"sim_dx_v1",
"sim_dx_v3",
}
// TestUpstreamTemplateParity guards the proxy/gateway template registration
// contract: the proxy only forwards to the gateway, so every template the
// gateway registers must also be registered here or RefreshChannel fails before
// any request is made.
func TestUpstreamTemplateParity(t *testing.T) {
for _, tempKey := range chanTemplateKeys {
tempKey := tempKey
t.Run(tempKey, func(t *testing.T) {
temp, ok := service.UpstreamAPI().GetTemp(tempKey)
if !ok || temp == nil {
t.Fatalf("template not registered in proxy: %s", tempKey)
}
config := &model.SimChannelConfigItemCache{
Template: &entity.TSimChannelTemplate{TempKey: tempKey},
Channel: &entity.TSimChannels{ChanKey: "parity-test"},
}
if instance := temp.NewTemp(context.Background(), config); instance == nil {
t.Fatalf("template failed to instantiate: %s", tempKey)
}
})
}
}
+27
View File
@@ -0,0 +1,27 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// TApi is the golang structure of table t_api for DAO operations like Where/Data.
type TApi struct {
g.Meta `orm:"table:t_api, do:true"`
Id any // ID
AppKey any // APPKey
AppSecret any // APPSecret
RateLimitPeriod any // 限流周期(秒,0:不限流)
RateLimitCount any // 限流次数
DeptId any // 所属机构
TenantId any // 租户ID
CreatedDept any // 创建部门
CreatedBy any // 创建者
CreatedAt *gtime.Time // 创建时间
UpdatedBy any // 更新者
UpdatedAt *gtime.Time // 更新时间
}
+35
View File
@@ -0,0 +1,35 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// TCustomer is the golang structure of table t_customer for DAO operations like Where/Data.
type TCustomer struct {
g.Meta `orm:"table:t_customer, do:true"`
Id any // ID
CKey any // 客户编号
CType any // 客户类型(0:企业,1:个人)
CName any // 客户名称
CShortName any // 客户简称
ContactUserName any // 联系人
ContactPhone any // 联系电话
LicenseNumber any // 统一社会信用代码
RegionCode any // 行政区域代码
Address any // 地址
DepositAmount any // 预存金额
CreditAmountTotal any // 已授信额度
CreditAmountUsed any // 已使用授信额度
DeptId any // 所属机构
TenantId any // 租户ID
CreatedDept any // 创建部门
CreatedBy any // 创建者
CreatedAt *gtime.Time // 创建时间
UpdatedBy any // 更新者
UpdatedAt *gtime.Time // 更新时间
}
+34
View File
@@ -0,0 +1,34 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// TSimAgentPackage is the golang structure of table t_sim_agent_package for DAO operations like Where/Data.
type TSimAgentPackage struct {
g.Meta `orm:"table:t_sim_agent_package, do:true"`
Id any // ID
PackageKey any // 套餐分类编码
PackageName any // 套餐分类名称
ParentPackageKey any // 上级套餐分类编码
PackageStatus any // 是否启用(0:未启用,1启用)
BillingMethod any // 计费方式(0:月,1:季,2:年)
ChannelPrice any // 渠道价格
MonthlyFee any // 月租费
PackageDataMb any // 套餐流量(MB)
PackageDataGb any // 套餐流量(GB)
ValidityPeriod any // 有效期
Description any // 描述信息
OrderNum any // 排序
TenantId any // 租户ID
CreatedDept any // 创建部门
CreatedBy any // 创建者
CreatedAt *gtime.Time // 创建时间
UpdatedBy any // 更新者
UpdatedAt *gtime.Time // 更新时间
}
+29
View File
@@ -0,0 +1,29 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// TSimAgentPacket is the golang structure of table t_sim_agent_packet for DAO operations like Where/Data.
type TSimAgentPacket struct {
g.Meta `orm:"table:t_sim_agent_packet, do:true"`
Id any // ID
PackageKey any // 套餐分类编码
PacketKey any // 套餐编码
PacketName any // 套餐名称
ValidityPeriod any // 套餐有效期(月份)
PacketStatus any // 是否启用(0:未启用,1启用)
DiscountPercent any // 折扣率(百分比)
Description any // 描述信息
TenantId any // 租户ID
CreatedDept any // 创建部门
CreatedBy any // 创建者
CreatedAt *gtime.Time // 创建时间
UpdatedBy any // 更新者
UpdatedAt *gtime.Time // 更新时间
}
+73
View File
@@ -0,0 +1,73 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// TSimCardInfo is the golang structure of table t_sim_card_info for DAO operations like Where/Data.
type TSimCardInfo struct {
g.Meta `orm:"table:t_sim_card_info, do:true"`
Id any // ID
Msisdn any // SIM号码
Iccid any // SIM卡号
Imsi any // SIM识别码
Imei any // IMEI
OpenDate *gtime.Time // 开卡日期(运营商)
ActivateDate *gtime.Time // 激活时间(运营商)
AllocationBatch any // 划拨批次
AllocationDate *gtime.Time // 划拨日期
ChanKey any // 通道编号
IsPool any // 是否流量池(0:否,1:是)
PoolKey any // 流量池编号
BatchKey any // 入库批次
BatchDate *gtime.Time // 入库日期
PackageKey any // 套餐分类编码
CurrPacketKey any // 当前套餐编码
AllowPacketChange any // 是否允许更改套餐(0:否 1:是)
CardStatus any // 卡状态
ValidityPeriod any // 有效期(月份)
ExpiryDate *gtime.Time // 套餐到期日期
UpstreamExpiryDate *gtime.Time // 上游到期时间(运营商)
IpType any // IP版本(人工录入)
NetModel any // 网络模式(人工录入)
RegionCode any // 行政区域代码(省市)
HasOffNet any // 是否开启达量断网
OffNetQuota any // 达量断网阈值(MB)
OffNetExpiryDate *gtime.Time // 达量断网过期时间
AgentShowField any // 客户展示字段设置
AgentShowValue any // 客户展示字段数据
HasTestPeriod any // 是否有测试期(0:无,1:有)
TestPeriod any // 测试期(天)
TestPeriodExpiryDate *gtime.Time // 测试期过期日期
HasSilentPeriod any // 是否有沉默期(0:无,1:有)
SilentPeriod any // 沉默期(月)
SilentPeriodExpiryDate *gtime.Time // 沉默期过期日期
HasAdditional any // 是否有附加费用(0:无 1:有)
AdditionalMultiplier any // 附加费用倍数
AdditionalAmount any // 附加金额
DataService any // 数据服务(0:关闭 1:开启)
DataTotalAmount any // 套餐流量(MB)
DataUseAmount any // 已用流量(MB)
SmsService any // 短信服务(0:关闭 1:开启)
SmsTotalAmount any // 套餐短信数量
SmsUseAmount any // 已用短信数量
VoiceService any // 语音服务(0:关闭 1:开启)
VoiceTotalAmount any // 套餐通话分钟数
VoiceUseAmount any // 已用通话分钟数
IntlRoamingService any // 国际漫游服务(0:关闭 1:开启)
IntlRoamingTotalAmount any // 套餐国际漫游流量(MB)
IntlRoamingUseAmount any // 已用国际漫游流量(MB)
Description any // 描述信息
DeptId any // 所属机构
TenantId any // 租户ID
CreatedDept any // 创建部门
CreatedBy any // 创建者
CreatedAt *gtime.Time // 创建时间
UpdatedBy any // 更新者
UpdatedAt *gtime.Time // 更新时间
}
@@ -0,0 +1,27 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// TSimChannelTemplate is the golang structure of table t_sim_channel_template for DAO operations like Where/Data.
type TSimChannelTemplate struct {
g.Meta `orm:"table:t_sim_channel_template, do:true"`
Id any // ID
TempKey any // 模板标识
TempName any // 模板名称
TempModel any // 模板模型
TempConfig any // 模板配置
TempStatus any // 模板状态(0未启用,1启用)
TenantId any // 租户ID
CreatedDept any // 创建部门
CreatedBy any // 创建者
CreatedAt *gtime.Time // 创建时间
UpdatedBy any // 更新者
UpdatedAt *gtime.Time // 更新时间
}
+35
View File
@@ -0,0 +1,35 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// TSimChannels is the golang structure of table t_sim_channels for DAO operations like Where/Data.
type TSimChannels struct {
g.Meta `orm:"table:t_sim_channels, do:true"`
Id any // ID
TempKey any // 模板标识
ChanKey any // 通道编号
ChanName any // 通道名称
ChanConfig any // 通道配置
ChanStatus any // 通道状态(0未启用,1启用)
ChanAlias any // 通道别名
RegionCode any // 通道行政区域代码(省市)
AppKey any // 应用标识
AppSecret any // 应用密钥
AccessToken any // 接口调用凭证
AccessTokenCreated *gtime.Time // 凭证申请时间
AccessTokenExpire *gtime.Time // 凭证过期时间
RefreshToken any // 刷新凭证
TenantId any // 租户ID
CreatedDept any // 创建部门
CreatedBy any // 创建者
CreatedAt *gtime.Time // 创建时间
UpdatedBy any // 更新者
UpdatedAt *gtime.Time // 更新时间
}
+25
View File
@@ -0,0 +1,25 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// TApi is the golang structure for table t_api.
type TApi struct {
Id int64 `json:"id" orm:"id" description:"ID"`
AppKey string `json:"appKey" orm:"app_key" description:"APPKey"`
AppSecret string `json:"appSecret" orm:"app_secret" description:"APPSecret"`
RateLimitPeriod int `json:"rateLimitPeriod" orm:"rate_limit_period" description:"限流周期(秒,0:不限流)"`
RateLimitCount int `json:"rateLimitCount" orm:"rate_limit_count" description:"限流次数"`
DeptId int64 `json:"deptId" orm:"dept_id" description:"所属机构"`
TenantId string `json:"tenantId" orm:"tenant_id" description:"租户ID"`
CreatedDept int64 `json:"createdDept" orm:"created_dept" description:"创建部门"`
CreatedBy int64 `json:"createdBy" orm:"created_by" description:"创建者"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:"更新者"`
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
}
+33
View File
@@ -0,0 +1,33 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// TCustomer is the golang structure for table t_customer.
type TCustomer struct {
Id int64 `json:"id" orm:"id" description:"ID"`
CKey string `json:"cKey" orm:"c_key" description:"客户编号"`
CType int `json:"cType" orm:"c_type" description:"客户类型(0:企业,1:个人)"`
CName string `json:"cName" orm:"c_name" description:"客户名称"`
CShortName string `json:"cShortName" orm:"c_short_name" description:"客户简称"`
ContactUserName string `json:"contactUserName" orm:"contact_user_name" description:"联系人"`
ContactPhone string `json:"contactPhone" orm:"contact_phone" description:"联系电话"`
LicenseNumber string `json:"licenseNumber" orm:"license_number" description:"统一社会信用代码"`
RegionCode string `json:"regionCode" orm:"region_code" description:"行政区域代码"`
Address string `json:"address" orm:"address" description:"地址"`
DepositAmount float64 `json:"depositAmount" orm:"deposit_amount" description:"预存金额"`
CreditAmountTotal float64 `json:"creditAmountTotal" orm:"credit_amount_total" description:"已授信额度"`
CreditAmountUsed float64 `json:"creditAmountUsed" orm:"credit_amount_used" description:"已使用授信额度"`
DeptId int64 `json:"deptId" orm:"dept_id" description:"所属机构"`
TenantId string `json:"tenantId" orm:"tenant_id" description:"租户ID"`
CreatedDept int64 `json:"createdDept" orm:"created_dept" description:"创建部门"`
CreatedBy int64 `json:"createdBy" orm:"created_by" description:"创建者"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:"更新者"`
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
}
@@ -0,0 +1,32 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// TSimAgentPackage is the golang structure for table t_sim_agent_package.
type TSimAgentPackage struct {
Id int64 `json:"id" orm:"id" description:"ID"`
PackageKey string `json:"packageKey" orm:"package_key" description:"套餐分类编码"`
PackageName string `json:"packageName" orm:"package_name" description:"套餐分类名称"`
ParentPackageKey string `json:"parentPackageKey" orm:"parent_package_key" description:"上级套餐分类编码"`
PackageStatus int `json:"packageStatus" orm:"package_status" description:"是否启用(0:未启用,1启用)"`
BillingMethod int `json:"billingMethod" orm:"billing_method" description:"计费方式(0:月,1:季,2:年)"`
ChannelPrice float64 `json:"channelPrice" orm:"channel_price" description:"渠道价格"`
MonthlyFee float64 `json:"monthlyFee" orm:"monthly_fee" description:"月租费"`
PackageDataMb float64 `json:"packageDataMb" orm:"package_data_mb" description:"套餐流量(MB)"`
PackageDataGb float64 `json:"packageDataGb" orm:"package_data_gb" description:"套餐流量(GB)"`
ValidityPeriod string `json:"validityPeriod" orm:"validity_period" description:"有效期"`
Description string `json:"description" orm:"description" description:"描述信息"`
OrderNum int `json:"orderNum" orm:"order_num" description:"排序"`
TenantId string `json:"tenantId" orm:"tenant_id" description:"租户ID"`
CreatedDept int64 `json:"createdDept" orm:"created_dept" description:"创建部门"`
CreatedBy int64 `json:"createdBy" orm:"created_by" description:"创建者"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:"更新者"`
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
}
@@ -0,0 +1,27 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// TSimAgentPacket is the golang structure for table t_sim_agent_packet.
type TSimAgentPacket struct {
Id int64 `json:"id" orm:"id" description:"ID"`
PackageKey string `json:"packageKey" orm:"package_key" description:"套餐分类编码"`
PacketKey string `json:"packetKey" orm:"packet_key" description:"套餐编码"`
PacketName string `json:"packetName" orm:"packet_name" description:"套餐名称"`
ValidityPeriod int `json:"validityPeriod" orm:"validity_period" description:"套餐有效期(月份)"`
PacketStatus int `json:"packetStatus" orm:"packet_status" description:"是否启用(0:未启用,1启用)"`
DiscountPercent int `json:"discountPercent" orm:"discount_percent" description:"折扣率(百分比)"`
Description string `json:"description" orm:"description" description:"描述信息"`
TenantId string `json:"tenantId" orm:"tenant_id" description:"租户ID"`
CreatedDept int64 `json:"createdDept" orm:"created_dept" description:"创建部门"`
CreatedBy int64 `json:"createdBy" orm:"created_by" description:"创建者"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:"更新者"`
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
}
+71
View File
@@ -0,0 +1,71 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// TSimCardInfo is the golang structure for table t_sim_card_info.
type TSimCardInfo struct {
Id int64 `json:"id" orm:"id" description:"ID"`
Msisdn string `json:"msisdn" orm:"msisdn" description:"SIM号码"`
Iccid string `json:"iccid" orm:"iccid" description:"SIM卡号"`
Imsi string `json:"imsi" orm:"imsi" description:"SIM识别码"`
Imei string `json:"imei" orm:"imei" description:"IMEI"`
OpenDate *gtime.Time `json:"openDate" orm:"open_date" description:"开卡日期(运营商)"`
ActivateDate *gtime.Time `json:"activateDate" orm:"activate_date" description:"激活时间(运营商)"`
AllocationBatch string `json:"allocationBatch" orm:"allocation_batch" description:"划拨批次"`
AllocationDate *gtime.Time `json:"allocationDate" orm:"allocation_date" description:"划拨日期"`
ChanKey string `json:"chanKey" orm:"chan_key" description:"通道编号"`
IsPool int `json:"isPool" orm:"is_pool" description:"是否流量池(0:否,1:是)"`
PoolKey string `json:"poolKey" orm:"pool_key" description:"流量池编号"`
BatchKey string `json:"batchKey" orm:"batch_key" description:"入库批次"`
BatchDate *gtime.Time `json:"batchDate" orm:"batch_date" description:"入库日期"`
PackageKey string `json:"packageKey" orm:"package_key" description:"套餐分类编码"`
CurrPacketKey string `json:"currPacketKey" orm:"curr_packet_key" description:"当前套餐编码"`
AllowPacketChange int `json:"allowPacketChange" orm:"allow_packet_change" description:"是否允许更改套餐(0:否 1:是)"`
CardStatus int `json:"cardStatus" orm:"card_status" description:"卡状态"`
ValidityPeriod int `json:"validityPeriod" orm:"validity_period" description:"有效期(月份)"`
ExpiryDate *gtime.Time `json:"expiryDate" orm:"expiry_date" description:"套餐到期日期"`
UpstreamExpiryDate *gtime.Time `json:"upstreamExpiryDate" orm:"upstream_expiry_date" description:"上游到期时间(运营商)"`
IpType string `json:"ipType" orm:"ip_type" description:"IP版本(人工录入)"`
NetModel string `json:"netModel" orm:"net_model" description:"网络模式(人工录入)"`
RegionCode string `json:"regionCode" orm:"region_code" description:"行政区域代码(省市)"`
HasOffNet int `json:"hasOffNet" orm:"has_off_net" description:"是否开启达量断网"`
OffNetQuota float64 `json:"offNetQuota" orm:"off_net_quota" description:"达量断网阈值(MB)"`
OffNetExpiryDate *gtime.Time `json:"offNetExpiryDate" orm:"off_net_expiry_date" description:"达量断网过期时间"`
AgentShowField string `json:"agentShowField" orm:"agent_show_field" description:"客户展示字段设置"`
AgentShowValue string `json:"agentShowValue" orm:"agent_show_value" description:"客户展示字段数据"`
HasTestPeriod int `json:"hasTestPeriod" orm:"has_test_period" description:"是否有测试期(0:无,1:有)"`
TestPeriod int `json:"testPeriod" orm:"test_period" description:"测试期(天)"`
TestPeriodExpiryDate *gtime.Time `json:"testPeriodExpiryDate" orm:"test_period_expiry_date" description:"测试期过期日期"`
HasSilentPeriod int `json:"hasSilentPeriod" orm:"has_silent_period" description:"是否有沉默期(0:无,1:有)"`
SilentPeriod int `json:"silentPeriod" orm:"silent_period" description:"沉默期(月)"`
SilentPeriodExpiryDate *gtime.Time `json:"silentPeriodExpiryDate" orm:"silent_period_expiry_date" description:"沉默期过期日期"`
HasAdditional int `json:"hasAdditional" orm:"has_additional" description:"是否有附加费用(0:无 1:有)"`
AdditionalMultiplier float64 `json:"additionalMultiplier" orm:"additional_multiplier" description:"附加费用倍数"`
AdditionalAmount float64 `json:"additionalAmount" orm:"additional_amount" description:"附加金额"`
DataService int `json:"dataService" orm:"data_service" description:"数据服务(0:关闭 1:开启)"`
DataTotalAmount float64 `json:"dataTotalAmount" orm:"data_total_amount" description:"套餐流量(MB)"`
DataUseAmount float64 `json:"dataUseAmount" orm:"data_use_amount" description:"已用流量(MB)"`
SmsService int `json:"smsService" orm:"sms_service" description:"短信服务(0:关闭 1:开启)"`
SmsTotalAmount float64 `json:"smsTotalAmount" orm:"sms_total_amount" description:"套餐短信数量"`
SmsUseAmount float64 `json:"smsUseAmount" orm:"sms_use_amount" description:"已用短信数量"`
VoiceService int `json:"voiceService" orm:"voice_service" description:"语音服务(0:关闭 1:开启)"`
VoiceTotalAmount float64 `json:"voiceTotalAmount" orm:"voice_total_amount" description:"套餐通话分钟数"`
VoiceUseAmount float64 `json:"voiceUseAmount" orm:"voice_use_amount" description:"已用通话分钟数"`
IntlRoamingService int `json:"intlRoamingService" orm:"intl_roaming_service" description:"国际漫游服务(0:关闭 1:开启)"`
IntlRoamingTotalAmount float64 `json:"intlRoamingTotalAmount" orm:"intl_roaming_total_amount" description:"套餐国际漫游流量(MB)"`
IntlRoamingUseAmount float64 `json:"intlRoamingUseAmount" orm:"intl_roaming_use_amount" description:"已用国际漫游流量(MB)"`
Description string `json:"description" orm:"description" description:"描述信息"`
DeptId int64 `json:"deptId" orm:"dept_id" description:"所属机构"`
TenantId string `json:"tenantId" orm:"tenant_id" description:"租户ID"`
CreatedDept int64 `json:"createdDept" orm:"created_dept" description:"创建部门"`
CreatedBy int64 `json:"createdBy" orm:"created_by" description:"创建者"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:"更新者"`
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
}
@@ -0,0 +1,25 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// TSimChannelTemplate is the golang structure for table t_sim_channel_template.
type TSimChannelTemplate struct {
Id int64 `json:"id" orm:"id" description:"ID"`
TempKey string `json:"tempKey" orm:"temp_key" description:"模板标识"`
TempName string `json:"tempName" orm:"temp_name" description:"模板名称"`
TempModel string `json:"tempModel" orm:"temp_model" description:"模板模型"`
TempConfig string `json:"tempConfig" orm:"temp_config" description:"模板配置"`
TempStatus int `json:"tempStatus" orm:"temp_status" description:"模板状态(0未启用,1启用)"`
TenantId string `json:"tenantId" orm:"tenant_id" description:"租户ID"`
CreatedDept int64 `json:"createdDept" orm:"created_dept" description:"创建部门"`
CreatedBy int64 `json:"createdBy" orm:"created_by" description:"创建者"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:"更新者"`
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
}
+33
View File
@@ -0,0 +1,33 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// TSimChannels is the golang structure for table t_sim_channels.
type TSimChannels struct {
Id int64 `json:"id" orm:"id" description:"ID"`
TempKey string `json:"tempKey" orm:"temp_key" description:"模板标识"`
ChanKey string `json:"chanKey" orm:"chan_key" description:"通道编号"`
ChanName string `json:"chanName" orm:"chan_name" description:"通道名称"`
ChanConfig string `json:"chanConfig" orm:"chan_config" description:"通道配置"`
ChanStatus int `json:"chanStatus" orm:"chan_status" description:"通道状态(0未启用,1启用)"`
ChanAlias string `json:"chanAlias" orm:"chan_alias" description:"通道别名"`
RegionCode string `json:"regionCode" orm:"region_code" description:"通道行政区域代码(省市)"`
AppKey string `json:"appKey" orm:"app_key" description:"应用标识"`
AppSecret string `json:"appSecret" orm:"app_secret" description:"应用密钥"`
AccessToken string `json:"accessToken" orm:"access_token" description:"接口调用凭证"`
AccessTokenCreated *gtime.Time `json:"accessTokenCreated" orm:"access_token_created" description:"凭证申请时间"`
AccessTokenExpire *gtime.Time `json:"accessTokenExpire" orm:"access_token_expire" description:"凭证过期时间"`
RefreshToken string `json:"refreshToken" orm:"refresh_token" description:"刷新凭证"`
TenantId string `json:"tenantId" orm:"tenant_id" description:"租户ID"`
CreatedDept int64 `json:"createdDept" orm:"created_dept" description:"创建部门"`
CreatedBy int64 `json:"createdBy" orm:"created_by" description:"创建者"`
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:"更新者"`
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
}
+43
View File
@@ -0,0 +1,43 @@
package model
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
"github.com/gogf/gf/v2/os/gtime"
)
type SimChannelConfigItemCache struct {
Template *entity.TSimChannelTemplate
Channel *entity.TSimChannels
IsNormal *int // global_yes_or_no: 0=yes, 1=no; NULL is not eligible.
Version string
}
// CanCreateInstance determines whether a channel may have a runtime instance.
func (c *SimChannelConfigItemCache) CanCreateInstance() bool {
return c != nil && c.Channel != nil && c.IsNormal != nil && *c.IsNormal == 0
}
type SimCardTempI interface {
NewTemp(ctx context.Context, channelConfig *SimChannelConfigItemCache) SimCardChannelI
}
// 接口定义
type SimCardChannelI interface {
SimBase(ctx context.Context, input *SimBaseReqJson) (SimBaseResJson, error)
}
type SimCardChannelCache struct {
Instance SimCardChannelI
Version string
}
type UpdateAccessTokenInput struct {
ChanKey string
AccessToken string
AccessTokenCreated *gtime.Time
AccessTokenExpire *gtime.Time
RefreshToken string
RefreshTokenExpire *gtime.Time
}
+36
View File
@@ -0,0 +1,36 @@
package model
import "encoding/json"
// 物联网卡标识
type SimKey struct {
ChanKey string `json:"chanKey" dc:"通道标识"`
ICCID string `json:"iccid" dc:"联网卡集成电路卡识别码20位"`
IMSI string `json:"imsi" dc:"国际移动用户识别码"`
MSISDN string `json:"msisdn" dc:"移动用户识别码"`
}
// 物联网卡基础请求参数
type SimBaseReq[T any] struct {
SimKeys []*SimKey `json:"simKeys" dc:"SIM卡标识列表"`
ReqData T `json:"reqData" dc:"请求数据"`
}
// 响应数据参数
type SimResponseData[T any] struct {
Code int `json:"code" dc:"状态码"`
Message string `json:"message" dc:"消息"`
Data T `json:"data" dc:"数据"`
}
// 物联网卡基础响应参数
type SimBaseRes[T any] struct {
Code int `json:"code" dc:"状态码"`
Message string `json:"message" dc:"消息"`
ErrCode string `json:"errCode" dc:"运营商原始错误码"`
SimKey *SimKey `json:"simKey" dc:"SIM卡标识"`
ResData T `json:"resData" dc:"响应数据"`
}
type SimBaseReqJson = SimBaseReq[json.RawMessage]
type SimBaseResJson = []*SimBaseRes[json.RawMessage]
+1
View File
@@ -0,0 +1 @@
package packed
+206
View File
@@ -0,0 +1,206 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v5.29.3
// source: common.proto
package pb
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type HeadReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
RouterId int64 `protobuf:"varint,1,opt,name=router_id,json=routerId,proto3" json:"router_id,omitempty" dc:"路由id json:'router_id'"` // 路由id json:"router_id"
TraceId string `protobuf:"bytes,2,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty" dc:"追踪id json:'trace_id'"` // 追踪id json:"trace_id"
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HeadReq) Reset() {
*x = HeadReq{}
mi := &file_common_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HeadReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HeadReq) ProtoMessage() {}
func (x *HeadReq) ProtoReflect() protoreflect.Message {
mi := &file_common_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HeadReq.ProtoReflect.Descriptor instead.
func (*HeadReq) Descriptor() ([]byte, []int) {
return file_common_proto_rawDescGZIP(), []int{0}
}
func (x *HeadReq) GetRouterId() int64 {
if x != nil {
return x.RouterId
}
return 0
}
func (x *HeadReq) GetTraceId() string {
if x != nil {
return x.TraceId
}
return ""
}
type HeadRes struct {
state protoimpl.MessageState `protogen:"open.v1"`
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty" dc:"错误码 json:'code'"` // 错误码 json:"code"
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty" dc:"错误信息 json:'message'"` // 错误信息 json:"message"
RouterId int64 `protobuf:"varint,3,opt,name=router_id,json=routerId,proto3" json:"router_id,omitempty" dc:"路由id json:'router_id'"` // 路由id json:"router_id"
TraceId string `protobuf:"bytes,4,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty" dc:"追踪id json:'trace_id'"` // 追踪id json:"trace_id"
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HeadRes) Reset() {
*x = HeadRes{}
mi := &file_common_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HeadRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HeadRes) ProtoMessage() {}
func (x *HeadRes) ProtoReflect() protoreflect.Message {
mi := &file_common_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HeadRes.ProtoReflect.Descriptor instead.
func (*HeadRes) Descriptor() ([]byte, []int) {
return file_common_proto_rawDescGZIP(), []int{1}
}
func (x *HeadRes) GetCode() int32 {
if x != nil {
return x.Code
}
return 0
}
func (x *HeadRes) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *HeadRes) GetRouterId() int64 {
if x != nil {
return x.RouterId
}
return 0
}
func (x *HeadRes) GetTraceId() string {
if x != nil {
return x.TraceId
}
return ""
}
var File_common_proto protoreflect.FileDescriptor
const file_common_proto_rawDesc = "" +
"\n" +
"\fcommon.proto\x12\bPbCommon\"A\n" +
"\aHeadReq\x12\x1b\n" +
"\trouter_id\x18\x01 \x01(\x03R\brouterId\x12\x19\n" +
"\btrace_id\x18\x02 \x01(\tR\atraceId\"o\n" +
"\aHeadRes\x12\x12\n" +
"\x04code\x18\x01 \x01(\x05R\x04code\x12\x18\n" +
"\amessage\x18\x02 \x01(\tR\amessage\x12\x1b\n" +
"\trouter_id\x18\x03 \x01(\x03R\brouterId\x12\x19\n" +
"\btrace_id\x18\x04 \x01(\tR\atraceIdB\x05Z\x03pb/b\x06proto3"
var (
file_common_proto_rawDescOnce sync.Once
file_common_proto_rawDescData []byte
)
func file_common_proto_rawDescGZIP() []byte {
file_common_proto_rawDescOnce.Do(func() {
file_common_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc)))
})
return file_common_proto_rawDescData
}
var file_common_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_common_proto_goTypes = []any{
(*HeadReq)(nil), // 0: PbCommon.HeadReq
(*HeadRes)(nil), // 1: PbCommon.HeadRes
}
var file_common_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_common_proto_init() }
func file_common_proto_init() {
if File_common_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_common_proto_goTypes,
DependencyIndexes: file_common_proto_depIdxs,
MessageInfos: file_common_proto_msgTypes,
}.Build()
File_common_proto = out.File
file_common_proto_goTypes = nil
file_common_proto_depIdxs = nil
}
+257
View File
@@ -0,0 +1,257 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v5.29.3
// source: info.proto
package pb
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type InfoReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
Head *HeadReq `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InfoReq) Reset() {
*x = InfoReq{}
mi := &file_info_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *InfoReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*InfoReq) ProtoMessage() {}
func (x *InfoReq) ProtoReflect() protoreflect.Message {
mi := &file_info_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use InfoReq.ProtoReflect.Descriptor instead.
func (*InfoReq) Descriptor() ([]byte, []int) {
return file_info_proto_rawDescGZIP(), []int{0}
}
func (x *InfoReq) GetHead() *HeadReq {
if x != nil {
return x.Head
}
return nil
}
type InfoRes struct {
state protoimpl.MessageState `protogen:"open.v1"`
Head *HeadRes `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"`
Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"`
Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"`
Build string `protobuf:"bytes,7,opt,name=build,proto3" json:"build,omitempty"`
Commit string `protobuf:"bytes,8,opt,name=commit,proto3" json:"commit,omitempty"`
Data map[string]string `protobuf:"bytes,9,rep,name=data,proto3" json:"data,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InfoRes) Reset() {
*x = InfoRes{}
mi := &file_info_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *InfoRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*InfoRes) ProtoMessage() {}
func (x *InfoRes) ProtoReflect() protoreflect.Message {
mi := &file_info_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use InfoRes.ProtoReflect.Descriptor instead.
func (*InfoRes) Descriptor() ([]byte, []int) {
return file_info_proto_rawDescGZIP(), []int{1}
}
func (x *InfoRes) GetHead() *HeadRes {
if x != nil {
return x.Head
}
return nil
}
func (x *InfoRes) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *InfoRes) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *InfoRes) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *InfoRes) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *InfoRes) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
func (x *InfoRes) GetBuild() string {
if x != nil {
return x.Build
}
return ""
}
func (x *InfoRes) GetCommit() string {
if x != nil {
return x.Commit
}
return ""
}
func (x *InfoRes) GetData() map[string]string {
if x != nil {
return x.Data
}
return nil
}
var File_info_proto protoreflect.FileDescriptor
const file_info_proto_rawDesc = "" +
"\n" +
"\n" +
"info.proto\x12\x06PbInfo\x1a\fcommon.proto\"0\n" +
"\aInfoReq\x12%\n" +
"\x04head\x18\x01 \x01(\v2\x11.PbCommon.HeadReqR\x04head\"\xc0\x02\n" +
"\aInfoRes\x12%\n" +
"\x04head\x18\x01 \x01(\v2\x11.PbCommon.HeadResR\x04head\x12\x12\n" +
"\x04type\x18\x02 \x01(\tR\x04type\x12\x12\n" +
"\x04name\x18\x03 \x01(\tR\x04name\x12\x14\n" +
"\x05title\x18\x04 \x01(\tR\x05title\x12 \n" +
"\vdescription\x18\x05 \x01(\tR\vdescription\x12\x18\n" +
"\aversion\x18\x06 \x01(\tR\aversion\x12\x14\n" +
"\x05build\x18\a \x01(\tR\x05build\x12\x16\n" +
"\x06commit\x18\b \x01(\tR\x06commit\x12-\n" +
"\x04data\x18\t \x03(\v2\x19.PbInfo.InfoRes.DataEntryR\x04data\x1a7\n" +
"\tDataEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x0122\n" +
"\x04Info\x12*\n" +
"\x04Info\x12\x0f.PbInfo.InfoReq\x1a\x0f.PbInfo.InfoRes\"\x00B\x05Z\x03pb/b\x06proto3"
var (
file_info_proto_rawDescOnce sync.Once
file_info_proto_rawDescData []byte
)
func file_info_proto_rawDescGZIP() []byte {
file_info_proto_rawDescOnce.Do(func() {
file_info_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_info_proto_rawDesc), len(file_info_proto_rawDesc)))
})
return file_info_proto_rawDescData
}
var file_info_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_info_proto_goTypes = []any{
(*InfoReq)(nil), // 0: PbInfo.InfoReq
(*InfoRes)(nil), // 1: PbInfo.InfoRes
nil, // 2: PbInfo.InfoRes.DataEntry
(*HeadReq)(nil), // 3: PbCommon.HeadReq
(*HeadRes)(nil), // 4: PbCommon.HeadRes
}
var file_info_proto_depIdxs = []int32{
3, // 0: PbInfo.InfoReq.head:type_name -> PbCommon.HeadReq
4, // 1: PbInfo.InfoRes.head:type_name -> PbCommon.HeadRes
2, // 2: PbInfo.InfoRes.data:type_name -> PbInfo.InfoRes.DataEntry
0, // 3: PbInfo.Info.Info:input_type -> PbInfo.InfoReq
1, // 4: PbInfo.Info.Info:output_type -> PbInfo.InfoRes
4, // [4:5] is the sub-list for method output_type
3, // [3:4] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_info_proto_init() }
func file_info_proto_init() {
if File_info_proto != nil {
return
}
file_common_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_info_proto_rawDesc), len(file_info_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_info_proto_goTypes,
DependencyIndexes: file_info_proto_depIdxs,
MessageInfos: file_info_proto_msgTypes,
}.Build()
File_info_proto = out.File
file_info_proto_goTypes = nil
file_info_proto_depIdxs = nil
}
+122
View File
@@ -0,0 +1,122 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
// - protoc v5.29.3
// source: info.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Info_Info_FullMethodName = "/PbInfo.Info/Info"
)
// InfoClient is the client API for Info service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type InfoClient interface {
Info(ctx context.Context, in *InfoReq, opts ...grpc.CallOption) (*InfoRes, error)
}
type infoClient struct {
cc grpc.ClientConnInterface
}
func NewInfoClient(cc grpc.ClientConnInterface) InfoClient {
return &infoClient{cc}
}
func (c *infoClient) Info(ctx context.Context, in *InfoReq, opts ...grpc.CallOption) (*InfoRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(InfoRes)
err := c.cc.Invoke(ctx, Info_Info_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// InfoServer is the server API for Info service.
// All implementations must embed UnimplementedInfoServer
// for forward compatibility.
type InfoServer interface {
Info(context.Context, *InfoReq) (*InfoRes, error)
mustEmbedUnimplementedInfoServer()
}
// UnimplementedInfoServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedInfoServer struct{}
func (UnimplementedInfoServer) Info(context.Context, *InfoReq) (*InfoRes, error) {
return nil, status.Error(codes.Unimplemented, "method Info not implemented")
}
func (UnimplementedInfoServer) mustEmbedUnimplementedInfoServer() {}
func (UnimplementedInfoServer) testEmbeddedByValue() {}
// UnsafeInfoServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to InfoServer will
// result in compilation errors.
type UnsafeInfoServer interface {
mustEmbedUnimplementedInfoServer()
}
func RegisterInfoServer(s grpc.ServiceRegistrar, srv InfoServer) {
// If the following call panics, it indicates UnimplementedInfoServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Info_ServiceDesc, srv)
}
func _Info_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(InfoReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InfoServer).Info(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Info_Info_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InfoServer).Info(ctx, req.(*InfoReq))
}
return interceptor(ctx, in, info, handler)
}
// Info_ServiceDesc is the grpc.ServiceDesc for Info service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Info_ServiceDesc = grpc.ServiceDesc{
ServiceName: "PbInfo.Info",
HandlerType: (*InfoServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Info",
Handler: _Info_Info_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "info.proto",
}
+632
View File
@@ -0,0 +1,632 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v5.29.3
// source: msgbus.proto
package pb
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type EnumMsgType int32
const (
EnumMsgType_MsgTypeNone EnumMsgType = 0 // 未知消息类型
EnumMsgType_MsgTypeLogin EnumMsgType = 1 // 客户端长连接登录包
EnumMsgType_MsgTypeLogout EnumMsgType = 2 // 客户端长连接登出包
EnumMsgType_MsgTypeConnect EnumMsgType = 3 // 新建链接消息
EnumMsgType_MsgTypeDisconnect EnumMsgType = 4 // 关闭链接消息
EnumMsgType_MsgTypeData EnumMsgType = 5 // 消息透传转发
)
// Enum value maps for EnumMsgType.
var (
EnumMsgType_name = map[int32]string{
0: "MsgTypeNone",
1: "MsgTypeLogin",
2: "MsgTypeLogout",
3: "MsgTypeConnect",
4: "MsgTypeDisconnect",
5: "MsgTypeData",
}
EnumMsgType_value = map[string]int32{
"MsgTypeNone": 0,
"MsgTypeLogin": 1,
"MsgTypeLogout": 2,
"MsgTypeConnect": 3,
"MsgTypeDisconnect": 4,
"MsgTypeData": 5,
}
)
func (x EnumMsgType) Enum() *EnumMsgType {
p := new(EnumMsgType)
*p = x
return p
}
func (x EnumMsgType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (EnumMsgType) Descriptor() protoreflect.EnumDescriptor {
return file_msgbus_proto_enumTypes[0].Descriptor()
}
func (EnumMsgType) Type() protoreflect.EnumType {
return &file_msgbus_proto_enumTypes[0]
}
func (x EnumMsgType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use EnumMsgType.Descriptor instead.
func (EnumMsgType) EnumDescriptor() ([]byte, []int) {
return file_msgbus_proto_rawDescGZIP(), []int{0}
}
// 渠道类型
type EnumChannelType int32
const (
EnumChannelType_ChannelTypeNone EnumChannelType = 0
EnumChannelType_ChannelTypeMqtt EnumChannelType = 1 // 1-MQTT服务器
EnumChannelType_ChannelTypeTcp EnumChannelType = 2 // 2-TCP服务器
EnumChannelType_ChannelTypeUdp EnumChannelType = 3 // 3-UDP服务器
EnumChannelType_ChannelTypeHttp EnumChannelType = 4 // 4-HTTP服务
EnumChannelType_ChannelTypeWs EnumChannelType = 5 // 5-WebSocket服务
EnumChannelType_ChannelTypeDb EnumChannelType = 6 // 6-数据库服务
EnumChannelType_ChannelTypeInner EnumChannelType = 100 // 100-内网流转通道
)
// Enum value maps for EnumChannelType.
var (
EnumChannelType_name = map[int32]string{
0: "ChannelTypeNone",
1: "ChannelTypeMqtt",
2: "ChannelTypeTcp",
3: "ChannelTypeUdp",
4: "ChannelTypeHttp",
5: "ChannelTypeWs",
6: "ChannelTypeDb",
100: "ChannelTypeInner",
}
EnumChannelType_value = map[string]int32{
"ChannelTypeNone": 0,
"ChannelTypeMqtt": 1,
"ChannelTypeTcp": 2,
"ChannelTypeUdp": 3,
"ChannelTypeHttp": 4,
"ChannelTypeWs": 5,
"ChannelTypeDb": 6,
"ChannelTypeInner": 100,
}
)
func (x EnumChannelType) Enum() *EnumChannelType {
p := new(EnumChannelType)
*p = x
return p
}
func (x EnumChannelType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (EnumChannelType) Descriptor() protoreflect.EnumDescriptor {
return file_msgbus_proto_enumTypes[1].Descriptor()
}
func (EnumChannelType) Type() protoreflect.EnumType {
return &file_msgbus_proto_enumTypes[1]
}
func (x EnumChannelType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use EnumChannelType.Descriptor instead.
func (EnumChannelType) EnumDescriptor() ([]byte, []int) {
return file_msgbus_proto_rawDescGZIP(), []int{1}
}
// 渠道信息
type Channel struct {
state protoimpl.MessageState `protogen:"open.v1"`
ChannelId int64 `protobuf:"varint,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" dc:"渠道ID"` // 渠道ID
ChannelType int32 `protobuf:"varint,2,opt,name=channel_type,json=channelType,proto3" json:"channel_type,omitempty" dc:"渠道类型 EnumChannelType"` // 渠道类型 EnumChannelType
ServerId int64 `protobuf:"varint,3,opt,name=server_id,json=serverId,proto3" json:"server_id,omitempty" dc:"服务ID"` // 服务ID
CreateTime int64 `protobuf:"varint,4,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" dc:"启动时间戳(秒)"` // 启动时间戳(秒)
TenantId string `protobuf:"bytes,5,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty" dc:"租户ID"` // 租户ID
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Channel) Reset() {
*x = Channel{}
mi := &file_msgbus_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Channel) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Channel) ProtoMessage() {}
func (x *Channel) ProtoReflect() protoreflect.Message {
mi := &file_msgbus_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Channel.ProtoReflect.Descriptor instead.
func (*Channel) Descriptor() ([]byte, []int) {
return file_msgbus_proto_rawDescGZIP(), []int{0}
}
func (x *Channel) GetChannelId() int64 {
if x != nil {
return x.ChannelId
}
return 0
}
func (x *Channel) GetChannelType() int32 {
if x != nil {
return x.ChannelType
}
return 0
}
func (x *Channel) GetServerId() int64 {
if x != nil {
return x.ServerId
}
return 0
}
func (x *Channel) GetCreateTime() int64 {
if x != nil {
return x.CreateTime
}
return 0
}
func (x *Channel) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
// 链接信息
type Conn struct {
state protoimpl.MessageState `protogen:"open.v1"`
ConnId int64 `protobuf:"varint,1,opt,name=conn_id,json=connId,proto3" json:"conn_id,omitempty" dc:"链接ID"` // 链接ID
CreateTime int64 `protobuf:"varint,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" dc:"链接时间戳(秒)"` // 链接时间戳(秒)
LastRecvTime int64 `protobuf:"varint,3,opt,name=last_recv_time,json=lastRecvTime,proto3" json:"last_recv_time,omitempty" dc:"最后接收时间戳(秒)"` // 最后接收时间戳(秒)
LastSendTime int64 `protobuf:"varint,4,opt,name=last_send_time,json=lastSendTime,proto3" json:"last_send_time,omitempty" dc:"最后发送时间戳(秒)"` // 最后发送时间戳(秒)
ClientId string `protobuf:"bytes,5,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" dc:"客户端ID"` // 客户端ID
UserName string `protobuf:"bytes,6,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty" dc:"客户端登录名称"` // 客户端登录名称
UserPass string `protobuf:"bytes,7,opt,name=user_pass,json=userPass,proto3" json:"user_pass,omitempty" dc:"客户端登录密码"` // 客户端登录密码
ProductKey string `protobuf:"bytes,8,opt,name=product_key,json=productKey,proto3" json:"product_key,omitempty" dc:"绑定主产品KEY"` // 绑定主产品KEY
DeviceKey string `protobuf:"bytes,9,opt,name=device_key,json=deviceKey,proto3" json:"device_key,omitempty" dc:"绑定主设备KEY"` // 绑定主设备KEY
RemoteAddr string `protobuf:"bytes,10,opt,name=remote_addr,json=remoteAddr,proto3" json:"remote_addr,omitempty"`
LocalAddr string `protobuf:"bytes,11,opt,name=local_addr,json=localAddr,proto3" json:"local_addr,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Conn) Reset() {
*x = Conn{}
mi := &file_msgbus_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Conn) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Conn) ProtoMessage() {}
func (x *Conn) ProtoReflect() protoreflect.Message {
mi := &file_msgbus_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Conn.ProtoReflect.Descriptor instead.
func (*Conn) Descriptor() ([]byte, []int) {
return file_msgbus_proto_rawDescGZIP(), []int{1}
}
func (x *Conn) GetConnId() int64 {
if x != nil {
return x.ConnId
}
return 0
}
func (x *Conn) GetCreateTime() int64 {
if x != nil {
return x.CreateTime
}
return 0
}
func (x *Conn) GetLastRecvTime() int64 {
if x != nil {
return x.LastRecvTime
}
return 0
}
func (x *Conn) GetLastSendTime() int64 {
if x != nil {
return x.LastSendTime
}
return 0
}
func (x *Conn) GetClientId() string {
if x != nil {
return x.ClientId
}
return ""
}
func (x *Conn) GetUserName() string {
if x != nil {
return x.UserName
}
return ""
}
func (x *Conn) GetUserPass() string {
if x != nil {
return x.UserPass
}
return ""
}
func (x *Conn) GetProductKey() string {
if x != nil {
return x.ProductKey
}
return ""
}
func (x *Conn) GetDeviceKey() string {
if x != nil {
return x.DeviceKey
}
return ""
}
func (x *Conn) GetRemoteAddr() string {
if x != nil {
return x.RemoteAddr
}
return ""
}
func (x *Conn) GetLocalAddr() string {
if x != nil {
return x.LocalAddr
}
return ""
}
type SendMsgReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
Head *HeadReq `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"`
Channel *Channel `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty" dc:"渠道信息"` // 渠道信息
Conn *Conn `protobuf:"bytes,3,opt,name=conn,proto3" json:"conn,omitempty" dc:"链接信息"` // 链接信息
MsgType int32 `protobuf:"varint,4,opt,name=msg_type,json=msgType,proto3" json:"msg_type,omitempty" dc:"消息类型"` // 消息类型
Topic string `protobuf:"bytes,5,opt,name=topic,proto3" json:"topic,omitempty"`
Data []byte `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SendMsgReq) Reset() {
*x = SendMsgReq{}
mi := &file_msgbus_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SendMsgReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SendMsgReq) ProtoMessage() {}
func (x *SendMsgReq) ProtoReflect() protoreflect.Message {
mi := &file_msgbus_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SendMsgReq.ProtoReflect.Descriptor instead.
func (*SendMsgReq) Descriptor() ([]byte, []int) {
return file_msgbus_proto_rawDescGZIP(), []int{2}
}
func (x *SendMsgReq) GetHead() *HeadReq {
if x != nil {
return x.Head
}
return nil
}
func (x *SendMsgReq) GetChannel() *Channel {
if x != nil {
return x.Channel
}
return nil
}
func (x *SendMsgReq) GetConn() *Conn {
if x != nil {
return x.Conn
}
return nil
}
func (x *SendMsgReq) GetMsgType() int32 {
if x != nil {
return x.MsgType
}
return 0
}
func (x *SendMsgReq) GetTopic() string {
if x != nil {
return x.Topic
}
return ""
}
func (x *SendMsgReq) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
type SendMsgRes struct {
state protoimpl.MessageState `protogen:"open.v1"`
Head *HeadRes `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"`
Conn *Conn `protobuf:"bytes,2,opt,name=conn,proto3" json:"conn,omitempty"`
Topic string `protobuf:"bytes,3,opt,name=topic,proto3" json:"topic,omitempty"`
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SendMsgRes) Reset() {
*x = SendMsgRes{}
mi := &file_msgbus_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SendMsgRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SendMsgRes) ProtoMessage() {}
func (x *SendMsgRes) ProtoReflect() protoreflect.Message {
mi := &file_msgbus_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SendMsgRes.ProtoReflect.Descriptor instead.
func (*SendMsgRes) Descriptor() ([]byte, []int) {
return file_msgbus_proto_rawDescGZIP(), []int{3}
}
func (x *SendMsgRes) GetHead() *HeadRes {
if x != nil {
return x.Head
}
return nil
}
func (x *SendMsgRes) GetConn() *Conn {
if x != nil {
return x.Conn
}
return nil
}
func (x *SendMsgRes) GetTopic() string {
if x != nil {
return x.Topic
}
return ""
}
func (x *SendMsgRes) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
var File_msgbus_proto protoreflect.FileDescriptor
const file_msgbus_proto_rawDesc = "" +
"\n" +
"\fmsgbus.proto\x12\bPbMsgBus\x1a\fcommon.proto\"\xa6\x01\n" +
"\aChannel\x12\x1d\n" +
"\n" +
"channel_id\x18\x01 \x01(\x03R\tchannelId\x12!\n" +
"\fchannel_type\x18\x02 \x01(\x05R\vchannelType\x12\x1b\n" +
"\tserver_id\x18\x03 \x01(\x03R\bserverId\x12\x1f\n" +
"\vcreate_time\x18\x04 \x01(\x03R\n" +
"createTime\x12\x1b\n" +
"\ttenant_id\x18\x05 \x01(\tR\btenantId\"\xe3\x02\n" +
"\x04Conn\x12\x17\n" +
"\aconn_id\x18\x01 \x01(\x03R\x06connId\x12\x1f\n" +
"\vcreate_time\x18\x02 \x01(\x03R\n" +
"createTime\x12$\n" +
"\x0elast_recv_time\x18\x03 \x01(\x03R\flastRecvTime\x12$\n" +
"\x0elast_send_time\x18\x04 \x01(\x03R\flastSendTime\x12\x1b\n" +
"\tclient_id\x18\x05 \x01(\tR\bclientId\x12\x1b\n" +
"\tuser_name\x18\x06 \x01(\tR\buserName\x12\x1b\n" +
"\tuser_pass\x18\a \x01(\tR\buserPass\x12\x1f\n" +
"\vproduct_key\x18\b \x01(\tR\n" +
"productKey\x12\x1d\n" +
"\n" +
"device_key\x18\t \x01(\tR\tdeviceKey\x12\x1f\n" +
"\vremote_addr\x18\n" +
" \x01(\tR\n" +
"remoteAddr\x12\x1d\n" +
"\n" +
"local_addr\x18\v \x01(\tR\tlocalAddr\"\xc9\x01\n" +
"\n" +
"SendMsgReq\x12%\n" +
"\x04head\x18\x01 \x01(\v2\x11.PbCommon.HeadReqR\x04head\x12+\n" +
"\achannel\x18\x02 \x01(\v2\x11.PbMsgBus.ChannelR\achannel\x12\"\n" +
"\x04conn\x18\x03 \x01(\v2\x0e.PbMsgBus.ConnR\x04conn\x12\x19\n" +
"\bmsg_type\x18\x04 \x01(\x05R\amsgType\x12\x14\n" +
"\x05topic\x18\x05 \x01(\tR\x05topic\x12\x12\n" +
"\x04data\x18\x06 \x01(\fR\x04data\"\x81\x01\n" +
"\n" +
"SendMsgRes\x12%\n" +
"\x04head\x18\x01 \x01(\v2\x11.PbCommon.HeadResR\x04head\x12\"\n" +
"\x04conn\x18\x02 \x01(\v2\x0e.PbMsgBus.ConnR\x04conn\x12\x14\n" +
"\x05topic\x18\x03 \x01(\tR\x05topic\x12\x12\n" +
"\x04data\x18\x04 \x01(\fR\x04data*\x7f\n" +
"\vEnumMsgType\x12\x0f\n" +
"\vMsgTypeNone\x10\x00\x12\x10\n" +
"\fMsgTypeLogin\x10\x01\x12\x11\n" +
"\rMsgTypeLogout\x10\x02\x12\x12\n" +
"\x0eMsgTypeConnect\x10\x03\x12\x15\n" +
"\x11MsgTypeDisconnect\x10\x04\x12\x0f\n" +
"\vMsgTypeData\x10\x05*\xb4\x01\n" +
"\x0fEnumChannelType\x12\x13\n" +
"\x0fChannelTypeNone\x10\x00\x12\x13\n" +
"\x0fChannelTypeMqtt\x10\x01\x12\x12\n" +
"\x0eChannelTypeTcp\x10\x02\x12\x12\n" +
"\x0eChannelTypeUdp\x10\x03\x12\x13\n" +
"\x0fChannelTypeHttp\x10\x04\x12\x11\n" +
"\rChannelTypeWs\x10\x05\x12\x11\n" +
"\rChannelTypeDb\x10\x06\x12\x14\n" +
"\x10ChannelTypeInner\x10d2x\n" +
"\x06MsgBus\x122\n" +
"\x04Send\x12\x14.PbMsgBus.SendMsgReq\x1a\x14.PbMsgBus.SendMsgRes\x12:\n" +
"\bLongSend\x12\x14.PbMsgBus.SendMsgReq\x1a\x14.PbMsgBus.SendMsgRes(\x010\x01B\x05Z\x03pb/b\x06proto3"
var (
file_msgbus_proto_rawDescOnce sync.Once
file_msgbus_proto_rawDescData []byte
)
func file_msgbus_proto_rawDescGZIP() []byte {
file_msgbus_proto_rawDescOnce.Do(func() {
file_msgbus_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_msgbus_proto_rawDesc), len(file_msgbus_proto_rawDesc)))
})
return file_msgbus_proto_rawDescData
}
var file_msgbus_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_msgbus_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_msgbus_proto_goTypes = []any{
(EnumMsgType)(0), // 0: PbMsgBus.EnumMsgType
(EnumChannelType)(0), // 1: PbMsgBus.EnumChannelType
(*Channel)(nil), // 2: PbMsgBus.Channel
(*Conn)(nil), // 3: PbMsgBus.Conn
(*SendMsgReq)(nil), // 4: PbMsgBus.SendMsgReq
(*SendMsgRes)(nil), // 5: PbMsgBus.SendMsgRes
(*HeadReq)(nil), // 6: PbCommon.HeadReq
(*HeadRes)(nil), // 7: PbCommon.HeadRes
}
var file_msgbus_proto_depIdxs = []int32{
6, // 0: PbMsgBus.SendMsgReq.head:type_name -> PbCommon.HeadReq
2, // 1: PbMsgBus.SendMsgReq.channel:type_name -> PbMsgBus.Channel
3, // 2: PbMsgBus.SendMsgReq.conn:type_name -> PbMsgBus.Conn
7, // 3: PbMsgBus.SendMsgRes.head:type_name -> PbCommon.HeadRes
3, // 4: PbMsgBus.SendMsgRes.conn:type_name -> PbMsgBus.Conn
4, // 5: PbMsgBus.MsgBus.Send:input_type -> PbMsgBus.SendMsgReq
4, // 6: PbMsgBus.MsgBus.LongSend:input_type -> PbMsgBus.SendMsgReq
5, // 7: PbMsgBus.MsgBus.Send:output_type -> PbMsgBus.SendMsgRes
5, // 8: PbMsgBus.MsgBus.LongSend:output_type -> PbMsgBus.SendMsgRes
7, // [7:9] is the sub-list for method output_type
5, // [5:7] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
}
func init() { file_msgbus_proto_init() }
func file_msgbus_proto_init() {
if File_msgbus_proto != nil {
return
}
file_common_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_msgbus_proto_rawDesc), len(file_msgbus_proto_rawDesc)),
NumEnums: 2,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_msgbus_proto_goTypes,
DependencyIndexes: file_msgbus_proto_depIdxs,
EnumInfos: file_msgbus_proto_enumTypes,
MessageInfos: file_msgbus_proto_msgTypes,
}.Build()
File_msgbus_proto = out.File
file_msgbus_proto_goTypes = nil
file_msgbus_proto_depIdxs = nil
}
+155
View File
@@ -0,0 +1,155 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
// - protoc v5.29.3
// source: msgbus.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
MsgBus_Send_FullMethodName = "/PbMsgBus.MsgBus/Send"
MsgBus_LongSend_FullMethodName = "/PbMsgBus.MsgBus/LongSend"
)
// MsgBusClient is the client API for MsgBus service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MsgBusClient interface {
Send(ctx context.Context, in *SendMsgReq, opts ...grpc.CallOption) (*SendMsgRes, error)
LongSend(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SendMsgReq, SendMsgRes], error)
}
type msgBusClient struct {
cc grpc.ClientConnInterface
}
func NewMsgBusClient(cc grpc.ClientConnInterface) MsgBusClient {
return &msgBusClient{cc}
}
func (c *msgBusClient) Send(ctx context.Context, in *SendMsgReq, opts ...grpc.CallOption) (*SendMsgRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SendMsgRes)
err := c.cc.Invoke(ctx, MsgBus_Send_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *msgBusClient) LongSend(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SendMsgReq, SendMsgRes], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &MsgBus_ServiceDesc.Streams[0], MsgBus_LongSend_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[SendMsgReq, SendMsgRes]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type MsgBus_LongSendClient = grpc.BidiStreamingClient[SendMsgReq, SendMsgRes]
// MsgBusServer is the server API for MsgBus service.
// All implementations must embed UnimplementedMsgBusServer
// for forward compatibility.
type MsgBusServer interface {
Send(context.Context, *SendMsgReq) (*SendMsgRes, error)
LongSend(grpc.BidiStreamingServer[SendMsgReq, SendMsgRes]) error
mustEmbedUnimplementedMsgBusServer()
}
// UnimplementedMsgBusServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMsgBusServer struct{}
func (UnimplementedMsgBusServer) Send(context.Context, *SendMsgReq) (*SendMsgRes, error) {
return nil, status.Error(codes.Unimplemented, "method Send not implemented")
}
func (UnimplementedMsgBusServer) LongSend(grpc.BidiStreamingServer[SendMsgReq, SendMsgRes]) error {
return status.Error(codes.Unimplemented, "method LongSend not implemented")
}
func (UnimplementedMsgBusServer) mustEmbedUnimplementedMsgBusServer() {}
func (UnimplementedMsgBusServer) testEmbeddedByValue() {}
// UnsafeMsgBusServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MsgBusServer will
// result in compilation errors.
type UnsafeMsgBusServer interface {
mustEmbedUnimplementedMsgBusServer()
}
func RegisterMsgBusServer(s grpc.ServiceRegistrar, srv MsgBusServer) {
// If the following call panics, it indicates UnimplementedMsgBusServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&MsgBus_ServiceDesc, srv)
}
func _MsgBus_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SendMsgReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MsgBusServer).Send(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MsgBus_Send_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MsgBusServer).Send(ctx, req.(*SendMsgReq))
}
return interceptor(ctx, in, info, handler)
}
func _MsgBus_LongSend_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(MsgBusServer).LongSend(&grpc.GenericServerStream[SendMsgReq, SendMsgRes]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type MsgBus_LongSendServer = grpc.BidiStreamingServer[SendMsgReq, SendMsgRes]
// MsgBus_ServiceDesc is the grpc.ServiceDesc for MsgBus service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var MsgBus_ServiceDesc = grpc.ServiceDesc{
ServiceName: "PbMsgBus.MsgBus",
HandlerType: (*MsgBusServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Send",
Handler: _MsgBus_Send_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "LongSend",
Handler: _MsgBus_LongSend_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "msgbus.proto",
}
+237
View File
@@ -0,0 +1,237 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v5.29.3
// source: msgcall.proto
package pb
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type MsgReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
Head *HeadReq `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"`
Service string `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"`
Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"`
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MsgReq) Reset() {
*x = MsgReq{}
mi := &file_msgcall_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *MsgReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MsgReq) ProtoMessage() {}
func (x *MsgReq) ProtoReflect() protoreflect.Message {
mi := &file_msgcall_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MsgReq.ProtoReflect.Descriptor instead.
func (*MsgReq) Descriptor() ([]byte, []int) {
return file_msgcall_proto_rawDescGZIP(), []int{0}
}
func (x *MsgReq) GetHead() *HeadReq {
if x != nil {
return x.Head
}
return nil
}
func (x *MsgReq) GetService() string {
if x != nil {
return x.Service
}
return ""
}
func (x *MsgReq) GetMethod() string {
if x != nil {
return x.Method
}
return ""
}
func (x *MsgReq) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
type MsgRes struct {
state protoimpl.MessageState `protogen:"open.v1"`
Head *HeadRes `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"`
Service string `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"`
Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"`
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MsgRes) Reset() {
*x = MsgRes{}
mi := &file_msgcall_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *MsgRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MsgRes) ProtoMessage() {}
func (x *MsgRes) ProtoReflect() protoreflect.Message {
mi := &file_msgcall_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MsgRes.ProtoReflect.Descriptor instead.
func (*MsgRes) Descriptor() ([]byte, []int) {
return file_msgcall_proto_rawDescGZIP(), []int{1}
}
func (x *MsgRes) GetHead() *HeadRes {
if x != nil {
return x.Head
}
return nil
}
func (x *MsgRes) GetService() string {
if x != nil {
return x.Service
}
return ""
}
func (x *MsgRes) GetMethod() string {
if x != nil {
return x.Method
}
return ""
}
func (x *MsgRes) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
var File_msgcall_proto protoreflect.FileDescriptor
const file_msgcall_proto_rawDesc = "" +
"\n" +
"\rmsgcall.proto\x12\tPbMsgCall\x1a\fcommon.proto\"u\n" +
"\x06MsgReq\x12%\n" +
"\x04head\x18\x01 \x01(\v2\x11.PbCommon.HeadReqR\x04head\x12\x18\n" +
"\aservice\x18\x02 \x01(\tR\aservice\x12\x16\n" +
"\x06method\x18\x03 \x01(\tR\x06method\x12\x12\n" +
"\x04data\x18\x04 \x01(\fR\x04data\"u\n" +
"\x06MsgRes\x12%\n" +
"\x04head\x18\x01 \x01(\v2\x11.PbCommon.HeadResR\x04head\x12\x18\n" +
"\aservice\x18\x02 \x01(\tR\aservice\x12\x16\n" +
"\x06method\x18\x03 \x01(\tR\x06method\x12\x12\n" +
"\x04data\x18\x04 \x01(\fR\x04data2r\n" +
"\aMsgCall\x12/\n" +
"\aOneCall\x12\x11.PbMsgCall.MsgReq\x1a\x11.PbMsgCall.MsgRes\x126\n" +
"\n" +
"StreamCall\x12\x11.PbMsgCall.MsgReq\x1a\x11.PbMsgCall.MsgRes(\x010\x01B\x05Z\x03pb/b\x06proto3"
var (
file_msgcall_proto_rawDescOnce sync.Once
file_msgcall_proto_rawDescData []byte
)
func file_msgcall_proto_rawDescGZIP() []byte {
file_msgcall_proto_rawDescOnce.Do(func() {
file_msgcall_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_msgcall_proto_rawDesc), len(file_msgcall_proto_rawDesc)))
})
return file_msgcall_proto_rawDescData
}
var file_msgcall_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_msgcall_proto_goTypes = []any{
(*MsgReq)(nil), // 0: PbMsgCall.MsgReq
(*MsgRes)(nil), // 1: PbMsgCall.MsgRes
(*HeadReq)(nil), // 2: PbCommon.HeadReq
(*HeadRes)(nil), // 3: PbCommon.HeadRes
}
var file_msgcall_proto_depIdxs = []int32{
2, // 0: PbMsgCall.MsgReq.head:type_name -> PbCommon.HeadReq
3, // 1: PbMsgCall.MsgRes.head:type_name -> PbCommon.HeadRes
0, // 2: PbMsgCall.MsgCall.OneCall:input_type -> PbMsgCall.MsgReq
0, // 3: PbMsgCall.MsgCall.StreamCall:input_type -> PbMsgCall.MsgReq
1, // 4: PbMsgCall.MsgCall.OneCall:output_type -> PbMsgCall.MsgRes
1, // 5: PbMsgCall.MsgCall.StreamCall:output_type -> PbMsgCall.MsgRes
4, // [4:6] is the sub-list for method output_type
2, // [2:4] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_msgcall_proto_init() }
func file_msgcall_proto_init() {
if File_msgcall_proto != nil {
return
}
file_common_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_msgcall_proto_rawDesc), len(file_msgcall_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_msgcall_proto_goTypes,
DependencyIndexes: file_msgcall_proto_depIdxs,
MessageInfos: file_msgcall_proto_msgTypes,
}.Build()
File_msgcall_proto = out.File
file_msgcall_proto_goTypes = nil
file_msgcall_proto_depIdxs = nil
}
+155
View File
@@ -0,0 +1,155 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
// - protoc v5.29.3
// source: msgcall.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
MsgCall_OneCall_FullMethodName = "/PbMsgCall.MsgCall/OneCall"
MsgCall_StreamCall_FullMethodName = "/PbMsgCall.MsgCall/StreamCall"
)
// MsgCallClient is the client API for MsgCall service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MsgCallClient interface {
OneCall(ctx context.Context, in *MsgReq, opts ...grpc.CallOption) (*MsgRes, error)
StreamCall(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[MsgReq, MsgRes], error)
}
type msgCallClient struct {
cc grpc.ClientConnInterface
}
func NewMsgCallClient(cc grpc.ClientConnInterface) MsgCallClient {
return &msgCallClient{cc}
}
func (c *msgCallClient) OneCall(ctx context.Context, in *MsgReq, opts ...grpc.CallOption) (*MsgRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgRes)
err := c.cc.Invoke(ctx, MsgCall_OneCall_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *msgCallClient) StreamCall(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[MsgReq, MsgRes], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &MsgCall_ServiceDesc.Streams[0], MsgCall_StreamCall_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[MsgReq, MsgRes]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type MsgCall_StreamCallClient = grpc.BidiStreamingClient[MsgReq, MsgRes]
// MsgCallServer is the server API for MsgCall service.
// All implementations must embed UnimplementedMsgCallServer
// for forward compatibility.
type MsgCallServer interface {
OneCall(context.Context, *MsgReq) (*MsgRes, error)
StreamCall(grpc.BidiStreamingServer[MsgReq, MsgRes]) error
mustEmbedUnimplementedMsgCallServer()
}
// UnimplementedMsgCallServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMsgCallServer struct{}
func (UnimplementedMsgCallServer) OneCall(context.Context, *MsgReq) (*MsgRes, error) {
return nil, status.Error(codes.Unimplemented, "method OneCall not implemented")
}
func (UnimplementedMsgCallServer) StreamCall(grpc.BidiStreamingServer[MsgReq, MsgRes]) error {
return status.Error(codes.Unimplemented, "method StreamCall not implemented")
}
func (UnimplementedMsgCallServer) mustEmbedUnimplementedMsgCallServer() {}
func (UnimplementedMsgCallServer) testEmbeddedByValue() {}
// UnsafeMsgCallServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MsgCallServer will
// result in compilation errors.
type UnsafeMsgCallServer interface {
mustEmbedUnimplementedMsgCallServer()
}
func RegisterMsgCallServer(s grpc.ServiceRegistrar, srv MsgCallServer) {
// If the following call panics, it indicates UnimplementedMsgCallServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&MsgCall_ServiceDesc, srv)
}
func _MsgCall_OneCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MsgReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MsgCallServer).OneCall(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MsgCall_OneCall_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MsgCallServer).OneCall(ctx, req.(*MsgReq))
}
return interceptor(ctx, in, info, handler)
}
func _MsgCall_StreamCall_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(MsgCallServer).StreamCall(&grpc.GenericServerStream[MsgReq, MsgRes]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type MsgCall_StreamCallServer = grpc.BidiStreamingServer[MsgReq, MsgRes]
// MsgCall_ServiceDesc is the grpc.ServiceDesc for MsgCall service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var MsgCall_ServiceDesc = grpc.ServiceDesc{
ServiceName: "PbMsgCall.MsgCall",
HandlerType: (*MsgCallServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "OneCall",
Handler: _MsgCall_OneCall_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "StreamCall",
Handler: _MsgCall_StreamCall_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "msgcall.proto",
}
+197
View File
@@ -0,0 +1,197 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v5.29.3
// source: notice.proto
package pb
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type SendReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
Head *HeadReq `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"`
Config []byte `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"`
Params []byte `protobuf:"bytes,3,opt,name=params,proto3" json:"params,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SendReq) Reset() {
*x = SendReq{}
mi := &file_notice_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SendReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SendReq) ProtoMessage() {}
func (x *SendReq) ProtoReflect() protoreflect.Message {
mi := &file_notice_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SendReq.ProtoReflect.Descriptor instead.
func (*SendReq) Descriptor() ([]byte, []int) {
return file_notice_proto_rawDescGZIP(), []int{0}
}
func (x *SendReq) GetHead() *HeadReq {
if x != nil {
return x.Head
}
return nil
}
func (x *SendReq) GetConfig() []byte {
if x != nil {
return x.Config
}
return nil
}
func (x *SendReq) GetParams() []byte {
if x != nil {
return x.Params
}
return nil
}
type SendRes struct {
state protoimpl.MessageState `protogen:"open.v1"`
Head *HeadRes `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SendRes) Reset() {
*x = SendRes{}
mi := &file_notice_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SendRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SendRes) ProtoMessage() {}
func (x *SendRes) ProtoReflect() protoreflect.Message {
mi := &file_notice_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SendRes.ProtoReflect.Descriptor instead.
func (*SendRes) Descriptor() ([]byte, []int) {
return file_notice_proto_rawDescGZIP(), []int{1}
}
func (x *SendRes) GetHead() *HeadRes {
if x != nil {
return x.Head
}
return nil
}
var File_notice_proto protoreflect.FileDescriptor
const file_notice_proto_rawDesc = "" +
"\n" +
"\fnotice.proto\x12\bPbNotice\x1a\fcommon.proto\"`\n" +
"\aSendReq\x12%\n" +
"\x04head\x18\x01 \x01(\v2\x11.PbCommon.HeadReqR\x04head\x12\x16\n" +
"\x06config\x18\x02 \x01(\fR\x06config\x12\x16\n" +
"\x06params\x18\x03 \x01(\fR\x06params\"0\n" +
"\aSendRes\x12%\n" +
"\x04head\x18\x01 \x01(\v2\x11.PbCommon.HeadResR\x04head28\n" +
"\x06Notice\x12.\n" +
"\x04Send\x12\x11.PbNotice.SendReq\x1a\x11.PbNotice.SendRes\"\x00B\x05Z\x03pb/b\x06proto3"
var (
file_notice_proto_rawDescOnce sync.Once
file_notice_proto_rawDescData []byte
)
func file_notice_proto_rawDescGZIP() []byte {
file_notice_proto_rawDescOnce.Do(func() {
file_notice_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_notice_proto_rawDesc), len(file_notice_proto_rawDesc)))
})
return file_notice_proto_rawDescData
}
var file_notice_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_notice_proto_goTypes = []any{
(*SendReq)(nil), // 0: PbNotice.SendReq
(*SendRes)(nil), // 1: PbNotice.SendRes
(*HeadReq)(nil), // 2: PbCommon.HeadReq
(*HeadRes)(nil), // 3: PbCommon.HeadRes
}
var file_notice_proto_depIdxs = []int32{
2, // 0: PbNotice.SendReq.head:type_name -> PbCommon.HeadReq
3, // 1: PbNotice.SendRes.head:type_name -> PbCommon.HeadRes
0, // 2: PbNotice.Notice.Send:input_type -> PbNotice.SendReq
1, // 3: PbNotice.Notice.Send:output_type -> PbNotice.SendRes
3, // [3:4] is the sub-list for method output_type
2, // [2:3] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_notice_proto_init() }
func file_notice_proto_init() {
if File_notice_proto != nil {
return
}
file_common_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_notice_proto_rawDesc), len(file_notice_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_notice_proto_goTypes,
DependencyIndexes: file_notice_proto_depIdxs,
MessageInfos: file_notice_proto_msgTypes,
}.Build()
File_notice_proto = out.File
file_notice_proto_goTypes = nil
file_notice_proto_depIdxs = nil
}
+122
View File
@@ -0,0 +1,122 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
// - protoc v5.29.3
// source: notice.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Notice_Send_FullMethodName = "/PbNotice.Notice/Send"
)
// NoticeClient is the client API for Notice service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type NoticeClient interface {
Send(ctx context.Context, in *SendReq, opts ...grpc.CallOption) (*SendRes, error)
}
type noticeClient struct {
cc grpc.ClientConnInterface
}
func NewNoticeClient(cc grpc.ClientConnInterface) NoticeClient {
return &noticeClient{cc}
}
func (c *noticeClient) Send(ctx context.Context, in *SendReq, opts ...grpc.CallOption) (*SendRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SendRes)
err := c.cc.Invoke(ctx, Notice_Send_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// NoticeServer is the server API for Notice service.
// All implementations must embed UnimplementedNoticeServer
// for forward compatibility.
type NoticeServer interface {
Send(context.Context, *SendReq) (*SendRes, error)
mustEmbedUnimplementedNoticeServer()
}
// UnimplementedNoticeServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedNoticeServer struct{}
func (UnimplementedNoticeServer) Send(context.Context, *SendReq) (*SendRes, error) {
return nil, status.Error(codes.Unimplemented, "method Send not implemented")
}
func (UnimplementedNoticeServer) mustEmbedUnimplementedNoticeServer() {}
func (UnimplementedNoticeServer) testEmbeddedByValue() {}
// UnsafeNoticeServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to NoticeServer will
// result in compilation errors.
type UnsafeNoticeServer interface {
mustEmbedUnimplementedNoticeServer()
}
func RegisterNoticeServer(s grpc.ServiceRegistrar, srv NoticeServer) {
// If the following call panics, it indicates UnimplementedNoticeServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Notice_ServiceDesc, srv)
}
func _Notice_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SendReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NoticeServer).Send(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Notice_Send_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NoticeServer).Send(ctx, req.(*SendReq))
}
return interceptor(ctx, in, info, handler)
}
// Notice_ServiceDesc is the grpc.ServiceDesc for Notice service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Notice_ServiceDesc = grpc.ServiceDesc{
ServiceName: "PbNotice.Notice",
HandlerType: (*NoticeServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Send",
Handler: _Notice_Send_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "notice.proto",
}
@@ -0,0 +1,32 @@
package initconsul
import (
"context"
"github.com/gogf/gf/contrib/registry/consul/v2"
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gsel"
)
func InitConsul(ctx context.Context) error {
// 初始化consul
err := registerConsul(ctx)
if err != nil {
return err
}
return nil
}
func registerConsul(ctx context.Context) error {
consulAddress := g.Cfg().MustGet(ctx, "registry.address", "127.0.0.1:8500").String()
g.Log().Infof(ctx, "consulAddress: %+v", consulAddress)
registry, err := consul.New(consul.WithAddress(consulAddress))
if err != nil {
g.Log().Fatalf(ctx, "consul初始化失败: %+v", err)
return err
}
grpcx.Resolver.Register(registry)
gsel.SetBuilder(gsel.NewBuilderRandom())
g.Log().Infof(ctx, "consul注册成功")
return nil
}
+51
View File
@@ -0,0 +1,51 @@
// package version
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <li@xiujie.cn>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package version
import (
"context"
"fmt"
"github.com/gogf/gf/v2/frame/g"
)
var (
BuildTime string
BuildVersion string
CommitID string
)
func ShowLogo(ctx context.Context, buildVersion, buildTime, commitID string) {
BuildVersion = buildVersion
BuildTime = buildTime
CommitID = commitID
//版本号
g.Log().Notice(ctx, `
██╗██╗███████╗ ██╗ ██████╗ ████████╗
██║██║██╔════╝ ██║██╔═══██╗╚══██╔══╝
██║██║█████╗ ██║██║ ██║ ██║
██ ██║██║██╔══╝ ██║██║ ██║ ██║
╚█████╔╝██║███████╗ ██║╚██████╔╝ ██║
╚════╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝
`)
g.Log().Notice(ctx, "Version :", buildVersion)
g.Log().Notice(ctx, "BuildTime :", buildTime)
g.Log().Notice(ctx, "CommitID :", commitID)
g.Log().Notice(ctx, "Copyright:", "2026 The iotLCM Authors")
fmt.Println("Version :", buildVersion)
fmt.Println("BuildTime :", buildTime)
fmt.Println("CommitID :", commitID)
fmt.Println("Copyright:", "2026 The iotLCM Authors")
}
func AppName(ctx context.Context) string {
return g.Cfg().MustGet(ctx, "system.appName", "IotLCM").String()
}
+14
View File
@@ -0,0 +1,14 @@
// Package xjpubconsts
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <li@xiujie.cn>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package xjpubconsts
const (
HTTPContentTypeXml = "text/xml"
HTTPContentTypeHtml = "text/html"
HTTPContentTypeStream = "text/event-stream"
HTTPContentTypeJson = "application/json"
HTTPContentTypeExcel = "application/octet-stream"
)
+17
View File
@@ -0,0 +1,17 @@
package xjpubconsts
const (
ServiceTypeWeb = "web" // web服务
ServiceTypeQueue = "queue" // 队列服务
ServiceTypeRedis = "redis" // 缓存类型
ServiceTypeNotice = "notice" // 通知服务
ServiceTypeTask = "task" // 任务服务
ServiceTypeAccess = "access" // 接入服务
)
const (
ServiceNameYzAdminWeb = "yzadmin-web-svc" // 后台管理服务
ServiceNameYzUpstreamChanSvc = "yzupstreamchan-grpc-svc" // 上游通道服务
ServiceNameYzUpstreamProxySvc = "yzupstreamproxy-grpc-svc" // 上游代理服务
ServiceNameYzApiHttpSvc = "yzapi-http-svc" // API 服务
)
@@ -0,0 +1,100 @@
package httpclient
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubservice"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/net/gsvc"
)
type sHttpClient struct {
client *gclient.Client
}
func NewHttpClient() *sHttpClient {
client := g.Client()
return &sHttpClient{
client: client,
}
}
func init() {
xjpubservice.RegisterHttpClient(NewHttpClient())
}
func (s *sHttpClient) PostObj(ctx context.Context, serviceName string, path string, req interface{}, res interface{}) (err error) {
reqJson, err := gjson.Marshal(req)
if err != nil {
return err
}
resJson, err := s.Post(ctx, serviceName, path, reqJson)
if err != nil {
return err
}
err = gjson.Unmarshal(resJson, res)
if err != nil {
g.Log().Errorf(ctx, "sHttpClient.PostObj gjson.Unmarshal error: %v, resJson: %s", err, string(resJson))
return err
}
g.Log().Debugf(ctx, "sHttpClient.PostObj res: %+v, resJson: %s", res, string(resJson))
return nil
}
func (s *sHttpClient) Post(ctx context.Context, serviceName string, path string, req []byte) (res []byte, err error) {
// s.client.SetHeader("Span-Id", "0.0.1")
// s.client.SetHeader("Trace-Id", gctx.CtxId(ctx))
url := "http://" + serviceName + path
s.client.SetDiscovery(gsvc.GetRegistry())
// g.Log().Debugf(ctx, "sHttpClient.Post url: %s, req: %+v", url, string(req))
response, err := s.client.Post(ctx, url, req)
if err != nil {
g.Log().Errorf(ctx, "sHttpClient.Post Post error: %v, url: %s, req: %+v", err, url, string(req))
return nil, err
}
defer response.Close()
res = response.ReadAll()
g.Log().Debugf(ctx, "sHttpClient.Post url: %s, req: %+v, res: %+v", url, string(req), string(res))
return res, nil
}
func (s *sHttpClient) AllServerPost(ctx context.Context, serviceName string, path string, req []byte) (resMap map[string][]byte, err error) {
// s.client.SetHeader("Span-Id", "0.0.1")
// s.client.SetHeader("Trace-Id", gctx.CtxId(ctx))
s.client.SetDiscovery(gsvc.GetRegistry())
servers, err := gsvc.Search(ctx, gsvc.SearchInput{
Name: serviceName,
})
if len(servers) == 0 {
g.Log().Errorf(ctx, "sHttpClient.AllServerPost gsvc.Search(ctx, serviceName) error: %v, serviceName: %s", err, serviceName)
return nil, err
}
// g.Log().Debugf(ctx, "sHttpClient.AllServerPost servers: %+v", servers)
endpoints := make([]string, 0)
for _, server := range servers {
endpoint := server.GetEndpoints()
if len(endpoint) == 0 {
g.Log().Errorf(ctx, "sHttpClient.AllServerPost server.GetEndpoints() error: %v, serviceName: %s", err, serviceName)
continue
}
endpoints = append(endpoints, endpoint[0].String())
}
resMap = make(map[string][]byte)
for _, e := range endpoints {
url := "http://" + e + path
res, err := s.Post(ctx, e, path, req)
// g.Log().Debugf(ctx, "sHttpClient.AllServerPost Post url: %s, req: %+v res: %+v err: %+v", url, string(req), string(res), err)
if err != nil {
resMap[e] = nil
g.Log().Errorf(ctx, "sHttpClient.AllServerPost Post error: %v, url: %s, req: %+v", err, url, string(req))
continue
}
resMap[e] = res
}
return resMap, nil
}
@@ -0,0 +1,37 @@
package httpclient
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubconsts"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubmodel"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubmodel/response"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubservice"
)
type sUpstreamProxyClient struct {
}
func NewUpstreamProxyClient() *sUpstreamProxyClient {
return &sUpstreamProxyClient{}
}
func init() {
xjpubservice.RegisterUpstreamProxyClient(NewUpstreamProxyClient())
}
// 物联网卡在线信息实时查询
func (s *sUpstreamProxyClient) QuerySimSession(ctx context.Context, input *xjpubmodel.SimSessionInput) (output *xjpubmodel.SimSessionOutput, err error) {
res := &response.ResponseGeneric[*xjpubmodel.SimSessionOutput]{}
err = xjpubservice.HttpClient().PostObj(ctx, xjpubconsts.ServiceNameYzUpstreamChanSvc, "/query/sim-session", input, res)
output = res.Data
return output, err
}
// 物联网卡卡状态查询
func (s *sUpstreamProxyClient) QuerySimCardStatus(ctx context.Context, input *xjpubmodel.SimCardStatusInput) (output *xjpubmodel.SimCardStatusOutput, err error) {
res := &response.ResponseGeneric[*xjpubmodel.SimCardStatusOutput]{}
err = xjpubservice.HttpClient().PostObj(ctx, xjpubconsts.ServiceNameYzUpstreamChanSvc, "/query/sim-status", input, res)
output = res.Data
return output, err
}
+68
View File
@@ -0,0 +1,68 @@
package info
import (
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubmodel"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubservice"
)
type sInfo struct {
Info xjpubmodel.InfoModel
}
func NewInfo() *sInfo {
return &sInfo{
Info: xjpubmodel.InfoModel{},
}
}
func init() {
xjpubservice.RegisterInfo(NewInfo())
}
func (s *sInfo) GetInfo() *xjpubmodel.InfoModel {
return &s.Info
}
func (s *sInfo) SetInfoName(Type, Name, Title, Description string) {
if s.Info.Name == "" {
s.Info.Type = Type
s.Info.Name = Name
s.Info.Title = Title
s.Info.Description = Description
}
}
func (s *sInfo) SetInfoBuild(buildVersion, buildTime, commitID string) {
s.Info.Version = buildVersion
s.Info.Build = buildTime
s.Info.Commit = commitID
}
func (s *sInfo) SetInfoData(data map[string]interface{}) {
s.Info.Data = data
}
func (s *sInfo) GetInfoData() map[string]interface{} {
return s.Info.Data
}
func (s *sInfo) AddInfoData(key string, value interface{}) {
if s.Info.Data == nil {
s.Info.Data = make(map[string]interface{})
}
s.Info.Data[key] = value
}
func (s *sInfo) DelInfoData(key string) {
delete(s.Info.Data, key)
}
func (s *sInfo) GetInfoDataByKey(key string) interface{} {
if s.Info.Data == nil {
return nil
}
if _, ok := s.Info.Data[key]; !ok {
return nil
}
return s.Info.Data[key]
}
+127
View File
@@ -0,0 +1,127 @@
package info
import (
"context"
"runtime"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubmodel"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubservice"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog"
)
type sInfoRpc struct {
Info *xjpubmodel.InfoModel
}
func NewInfoRpc() *sInfoRpc {
return &sInfoRpc{
Info: xjpubmodel.NewInfoModel(),
}
}
func init() {
xjpubservice.RegisterInfoRpc(NewInfoRpc())
}
func (s *sInfoRpc) GetInfo() *xjpubmodel.InfoModel {
ctx := context.Background()
logLevel := g.Cfg().MustGet(ctx, "logger.level", "").String()
if logLevel != "" {
g.Log().SetLevelStr(logLevel)
}
s.AddInfoData("log_level_conf", logLevel)
s.AddInfoData("log_level_real", toLogLevelStr(g.Log().GetLevel()))
s.AddInfoData("Goroutines", runtime.NumGoroutine())
var m runtime.MemStats
runtime.ReadMemStats(&m)
s.AddInfoData("MemAllocKB", m.Alloc/1024)
s.AddInfoData("MemTotalAllocKB", m.TotalAlloc/1024)
s.AddInfoData("MemSysKB", m.Sys/1024)
s.AddInfoData("MemHeapAllocKB", m.HeapAlloc/1024)
s.AddInfoData("MemHeapSysKB", m.HeapSys/1024)
s.AddInfoData("MemHeapIdleKB", m.HeapIdle/1024)
s.AddInfoData("MemHeapInuseKB", m.HeapInuse/1024)
s.AddInfoData("MemHeapReleasedKB", m.HeapReleased/1024)
s.AddInfoData("MemHeapObjects", m.HeapObjects)
s.AddInfoData("MemStackInuseKB", m.StackInuse/1024)
s.AddInfoData("MemStackSysKB", m.StackSys/1024)
s.AddInfoData("MemNumGC", m.NumGC)
return s.Info
}
func toLogLevelStr(level int) string {
if level == glog.LEVEL_NONE {
return "NONE"
}
if level == glog.LEVEL_ALL {
return "ALL"
}
if level == glog.LEVEL_DEV {
return "DEV"
}
if level == glog.LEVEL_PROD {
return "PROD"
}
str := ""
if level&glog.LEVEL_DEBU != 0 {
str += "DEBUG "
}
if level&glog.LEVEL_INFO != 0 {
str += "INFO "
}
if level&glog.LEVEL_NOTI != 0 {
str += "NOTI "
}
if level&glog.LEVEL_WARN != 0 {
str += "WARN "
}
if level&glog.LEVEL_ERRO != 0 {
str += "ERRO "
}
if level&glog.LEVEL_CRIT != 0 {
str += "CRIT "
}
if level&glog.LEVEL_PANI != 0 {
str += "PANI "
}
if level&glog.LEVEL_FATA != 0 {
str += "FATA "
}
return str
}
func (s *sInfoRpc) SetInfoName(Type, Name, Title, Description string) {
if s.Info.Name == "" {
s.Info.Type = Type
s.Info.Name = Name
s.Info.Title = Title
s.Info.Description = Description
}
}
func (s *sInfoRpc) SetInfoBuild(buildVersion, buildTime, commitID string) {
s.Info.Version = buildVersion
s.Info.Build = buildTime
s.Info.Commit = commitID
}
func (s *sInfoRpc) SetInfoData(data map[string]interface{}) {
s.Info.Data = data
}
func (s *sInfoRpc) GetInfoData() map[string]interface{} {
return s.Info.Data
}
func (s *sInfoRpc) AddInfoData(key string, value interface{}) {
s.Info.Data[key] = value
}
func (s *sInfoRpc) DelInfoData(key string) {
delete(s.Info.Data, key)
}
func (s *sInfoRpc) GetInfoDataByKey(key string) interface{} {
return s.Info.Data[key]
}
@@ -0,0 +1,118 @@
// package middleware
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <li@xiujie.cn>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package middleware
import (
"context"
"net/http"
"slices"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/util/gmeta"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubconsts"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubmodel/response"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubservice"
)
type sMiddleware struct {
}
func New() *sMiddleware {
return &sMiddleware{}
}
func init() {
xjpubservice.RegisterMiddleware(New())
}
func getContentType(r *ghttp.Request) (contentType string) {
contentType = r.Response.Header().Get("Content-Type")
if contentType != "" {
return
}
mime := gmeta.Get(r.GetHandlerResponse(), "mime").String()
if mime == "" {
contentType = xjpubconsts.HTTPContentTypeJson
} else {
contentType = mime
}
return
}
// 响应处理中间件
func (m *sMiddleware) ResponseHandler(r *ghttp.Request) {
r.Middleware.Next()
if r.Response.BufferLength() > 0 {
return
}
contentType := getContentType(r)
if contentType == xjpubconsts.HTTPContentTypeExcel {
r.Response.Status = http.StatusOK
return
}
// g.Log().Infof(r.GetCtx(), "sMiddleware.ResponseHandler,contentType %v", contentType)
err := r.GetError()
res := r.GetHandlerResponse()
var code gcode.Code = gcode.CodeOK
if err != nil {
code = gerror.Code(err)
if code == gcode.CodeNil {
code = gcode.CodeInternalError
}
response.JsonExit(r, code.Code(), err.Error())
} else {
response.Json(r, code.Code(), "", res)
}
}
// CORS 跨域处理
func (s *sMiddleware) CORS(r *ghttp.Request) {
r.SetCtx(r.GetNeverDoneCtx())
corsOptions := r.Response.DefaultCORSOptions()
corsConfig := g.Cfg().MustGet(context.Background(), "server.allowedDomains").Strings()
if len(corsConfig) == 0 {
r.Response.CORSDefault()
} else {
corsOptions.AllowDomain = corsConfig
r.Response.CORS(corsOptions)
}
r.Middleware.Next()
}
// IsExceptLogin 判断是否需要验证登录
func (s *sMiddleware) IsExceptLogin(ctx context.Context, path string) bool {
pathList := g.Cfg().MustGet(ctx, "router.exceptLogin").Strings()
return slices.Contains(pathList, path)
}
// ResponseHandlerRaw 返回处理中间件
func (s *sMiddleware) ResponseHandlerRaw(r *ghttp.Request) {
r.Middleware.Next()
// 如果已经有返回内容,那么该中间件什么也不做
if r.Response.BufferLength() > 0 {
g.Log().Infof(r.Context(), "ResponseHandler r.Response.BufferLength() %d buf:%s", r.Response.BufferLength(), r.Response.BufferString())
return
}
var (
err = r.GetError()
res = r.GetHandlerResponse()
)
if err != nil {
r.Response.WriteJson(res)
r.ExitAll()
} else {
r.Response.WriteJson(res)
}
}
+11
View File
@@ -0,0 +1,11 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package xjpublogic
import (
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpublogic/httpclient"
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpublogic/info"
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpublogic/middleware"
)
+18
View File
@@ -0,0 +1,18 @@
package xjpubmodel
type InfoModel struct {
Type string `json:"type"`
Name string `json:"name"`
Title string `json:"title"`
Description string `json:"description"`
Version string `json:"version"`
Build string `json:"build"`
Commit string `json:"commit"`
Data map[string]interface{} `json:"data"`
}
func NewInfoModel() *InfoModel {
return &InfoModel{
Data: make(map[string]interface{}),
}
}
@@ -0,0 +1,12 @@
// package response
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <li@xiujie.cn>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package response
type PageResult struct {
Page int `json:"page"`
PageSize int `json:"pageSize"`
Total int `json:"total"`
}
@@ -0,0 +1,67 @@
// package response
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <li@xiujie.cn>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package response
import (
"fmt"
"io"
"net/http"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
)
// 泛型
type ResponseGeneric[T any] struct {
Code int `json:"code"`
Message string `json:"message"`
Data T `json:"data"`
}
type JsonResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
Error interface{} `json:"error"`
}
func Json(r *ghttp.Request, code int, message string, data ...interface{}) {
var responseData interface{}
if len(data) > 0 {
responseData = data[0]
} else {
responseData = g.Map{}
}
// g.Log().Infof(r.GetCtx(), "response.Json: %v", responseData)
r.Response.WriteJson(JsonResponse{
Code: code,
Message: message,
Data: responseData,
})
}
func JsonExit(r *ghttp.Request, code int, message string, data ...interface{}) {
Json(r, code, message, data...)
r.ExitAll()
}
// 向前端返回Excel文件 参数 content 为上面生成的io.ReadSeeker, fileTag 为返回前端的文件名
func Excel(r *ghttp.Request, content io.ReadSeeker, fileTag string) {
fileName := fmt.Sprintf("%s%s%s.xlsx", gtime.Now().Format("Ymdhis"), `_`, fileTag)
r.Response.Header().Set("Content-Type", "application/vnd.ms-excel")
r.Response.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
http.ServeContent(r.Response.Writer, r.Request, fileTag, time.Now(), content)
}
// 向前端返回Json文件 参数 content 为上面生成的io.ReadSeeker, fileTag 为返回前端的文件名
func JsonFile(r *ghttp.Request, content io.ReadSeeker, fileTag string) {
fileName := fmt.Sprintf("%s%s%s.json", gtime.Now().Format("Ymdhis"), `_`, fileTag)
r.Response.Header().Set("Content-Type", "application/json")
r.Response.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
http.ServeContent(r.Response.Writer, r.Request, fileTag, time.Now(), content)
}
+48
View File
@@ -0,0 +1,48 @@
package xjpubmodel
type SimKey struct {
ChanKey string `json:"chanKey" dc:"通道标识"`
ICCID string `json:"iccid" dc:"联网卡集成电路卡识别码20位"`
}
// 物联网卡在线信息实时查询 请求参数
type SimSessionInput struct {
SimKeys []*SimKey `json:"simKeys" dc:"SIM卡标识列表"`
}
type SimSessionItem struct {
*SimKey
ApnId string `json:"apnId" dc:"APN ID"`
Rat string `json:"rat" dc:"接入方式"`
Ipv4 string `json:"ipv4" dc:"IPV4"`
Ipv6 string `json:"ipv6" dc:"IPV6"`
Ipv6Prefix string `json:"ipv6Prefix" dc:"IPV6前缀"`
Status string `json:"status" dc:"状态(0:用户在线 -1:用户不在线 -2:未查询到会话信息)"`
CreateTime string `json:"createTime" dc:"创建时间"`
ApiStatus int `json:"apiStatus" dc:"API状态"`
ApiMessage string `json:"apiMessage" dc:"API消息"`
}
// 物联网卡在线信息实时查询 响应参数
type SimSessionOutput struct {
Items []*SimSessionItem `json:"items" dc:"SIM卡会话列表"`
}
// 物联网卡卡状态查询 请求参数
type SimCardStatusInput struct {
SimKeys []*SimKey `json:"simKeys" dc:"SIM卡标识列表"`
}
// 物联网卡卡状态查询 响应参数
type SimCardStatusOutput struct {
Items []*SimCardStatusItem `json:"items" dc:"SIM卡状态列表"`
}
// {"cardStatus":"2","lastChangeDate":"2024-05-30 22:05:31"}]
type SimCardStatusItem struct {
*SimKey
CardStatus int `json:"cardStatus" dc:"卡状态"`
LastChangeDate string `json:"lastChangeDate" dc:"最后变更时间"`
ApiStatus int `json:"apiStatus" dc:"API状态"`
ApiMessage string `json:"apiMessage" dc:"API消息"`
}
+91
View File
@@ -0,0 +1,91 @@
package xjpubmodel
import (
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
)
type MetricItem struct {
Name string `json:"name"`
Value float64 `json:"value"`
Time *gtime.Time `json:"time"`
}
func (m *MetricItem) SetValue(value interface{}) {
m.Value = gconv.Float64(value)
m.Time = gtime.Now()
}
func (m *MetricItem) GetValue() float64 {
return m.Value
}
func (m *MetricItem) GetTime() *gtime.Time {
return m.Time
}
func (m *MetricItem) GetName() string {
return m.Name
}
func (m *MetricItem) SetName(name string) {
m.Name = name
}
func (m *MetricItem) AddValue(value interface{}) {
m.Value += gconv.Float64(value)
m.Time = gtime.Now()
}
func (m *MetricItem) Inc() {
m.Value++
m.Time = gtime.Now()
}
func (m *MetricItem) Dec() {
m.Value--
m.Time = gtime.Now()
}
type MetricData struct {
MetricClientCount MetricItem `json:"clientCount"` // 当前在线数
MetricConnectedCounter MetricItem `json:"connectedCounter"` // 连接数
MetricDisconnectedCounter MetricItem `json:"disconnectedCounter"` // 断开连接数
MetricRecvPkgCount MetricItem `json:"recvPkgCount"` // 接收包数
MetricSendPkgCount MetricItem `json:"sendPkgCount"` // 发送包数
MetricRecvPkgSize MetricItem `json:"recvPkgSize"` // 接收包大小
MetricSendPkgSize MetricItem `json:"sendPkgSize"` // 发送包大小
MetricRecvPkgTime MetricItem `json:"recvPkgTime"` // 接收包时间(毫秒)
MetricSendPkgTime MetricItem `json:"sendPkgTime"` // 发送包时间(毫秒)
MetricSubTopicCount MetricItem `json:"subTopicCount"` // 订阅主题数
MetricAuthSuccessCount MetricItem `json:"authSuccessCount"` // 认证成功数
MetricAuthFailCount MetricItem `json:"authFailCount"` // 认证失败数
MetricAuthCostTime MetricItem `json:"authCostTime"` // 认证耗时
MetricUpMsgConnectCostTime MetricItem `json:"upMsgConnectCostTime"` // 上行消息连接耗时(毫秒)
MetricUpMsgDataCostTime MetricItem `json:"upMsgDataCostTime"` // 上行消息数据耗时(毫秒)
MetricUpMsgDisconnectCostTime MetricItem `json:"upMsgDisconnectCostTime"` // 上行消息断开连接耗时(毫秒)
MetricTopicCount MetricItem `json:"topicCount"` // 累计主题数
MetricProcessMemSize MetricItem `json:"processMemSize"` // 进程内存大小
MetricProcessCpuUsage MetricItem `json:"processCpuUsage"` // 进程CPU使用率
MetricProcessThreads MetricItem `json:"processThreads"` // 进程线程数
MetricProcessGoroutines MetricItem `json:"processGoroutines"` // 进程协程数
MetricSysCpuNum MetricItem `json:"sysCpuNum"` // 系统CPU数量
MetricSysCpuCores MetricItem `json:"sysCpuCores"` // 系统CPU核数
MetricSysCpuUsage MetricItem `json:"sysCpuUsage"` // 系统CPU使用率
MetricSysCpuLoad1 MetricItem `json:"sysCpuLoad1"` // 系统CPU负载1
MetricSysCpuLoad5 MetricItem `json:"sysCpuLoad5"` // 系统CPU负载2
MetricSysCpuLoad15 MetricItem `json:"sysCpuLoad15"` // 系统CPU负载3
MetricSysMemTotal MetricItem `json:"sysMemTotal"` // 系统内存总量
MetricSysMemUsage MetricItem `json:"sysMemUsage"` // 系统内存使用率
MetricSysDiskTotal MetricItem `json:"sysDiskTotal"` // 系统磁盘总量
MetricSysDiskUsage MetricItem `json:"sysDiskUsage"` // 系统磁盘使用率
MetricWaitMsgCount MetricItem `json:"waitMsgCount"` // 等待消息数量
MetricOneDeviceWaitMsgCount MetricItem `json:"oneDeviceWaitMsgCount"` // 同一个设备等待消息数量
MetricRecvMsgFailCount MetricItem `json:"recvMsgFailCount"` // 接收消息处理失败数
MetricRecvMsgWaitCostTime MetricItem `json:"recvMsgWaitCostTime"` // 接收消息等待耗时
}
type XjServerInfo struct {
NodeName string `json:"nodeName"`
SvrType int `json:"svrType"`
SvrCode string `json:"svrCode"` // 服务器编号
ShardId int64 `json:"shardId"` // 分片ID
StartTime *gtime.Time `json:"startTime"` // 启动时间
SysIpLan string `json:"sysIpLan"` // 内网IP
SysIpWan string `json:"sysIpWan"` // 外网IP
AppVersion string `json:"appVersion"` // 程序版本
TenantId string `json:"tenantId"` // 租户ID
ReportInterval int64 `json:"reportInterval"` // 上报间隔
}
+53
View File
@@ -0,0 +1,53 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package xjpubservice
import (
"context"
xjpubmodel "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubmodel"
)
type (
IHttpClient interface {
PostObj(ctx context.Context, serviceName string, path string, req interface{}, res interface{}) (err error)
Post(ctx context.Context, serviceName string, path string, req []byte) (res []byte, err error)
AllServerPost(ctx context.Context, serviceName string, path string, req []byte) (resMap map[string][]byte, err error)
}
IUpstreamProxyClient interface {
// 物联网卡在线信息实时查询
QuerySimSession(ctx context.Context, req *xjpubmodel.SimSessionInput) (res *xjpubmodel.SimSessionOutput, err error)
// 物联网卡卡状态查询
QuerySimCardStatus(ctx context.Context, req *xjpubmodel.SimCardStatusInput) (res *xjpubmodel.SimCardStatusOutput, err error)
}
)
var (
localHttpClient IHttpClient
localUpstreamProxyClient IUpstreamProxyClient
)
func HttpClient() IHttpClient {
if localHttpClient == nil {
panic("implement not found for interface IHttpClient, forgot register?")
}
return localHttpClient
}
func RegisterHttpClient(i IHttpClient) {
localHttpClient = i
}
func UpstreamProxyClient() IUpstreamProxyClient {
if localUpstreamProxyClient == nil {
panic("implement not found for interface IUpstreamProxyClient, forgot register?")
}
return localUpstreamProxyClient
}
func RegisterUpstreamProxyClient(i IUpstreamProxyClient) {
localUpstreamProxyClient = i
}
+60
View File
@@ -0,0 +1,60 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package xjpubservice
import (
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/pub/xjpubmodel"
)
type (
IInfo interface {
GetInfo() *xjpubmodel.InfoModel
SetInfoName(Type string, Name string, Title string, Description string)
SetInfoBuild(buildVersion string, buildTime string, commitID string)
SetInfoData(data map[string]interface{})
GetInfoData() map[string]interface{}
AddInfoData(key string, value interface{})
DelInfoData(key string)
GetInfoDataByKey(key string) interface{}
}
IInfoRpc interface {
GetInfo() *xjpubmodel.InfoModel
SetInfoName(Type string, Name string, Title string, Description string)
SetInfoBuild(buildVersion string, buildTime string, commitID string)
SetInfoData(data map[string]interface{})
GetInfoData() map[string]interface{}
AddInfoData(key string, value interface{})
DelInfoData(key string)
GetInfoDataByKey(key string) interface{}
}
)
var (
localInfo IInfo
localInfoRpc IInfoRpc
)
func Info() IInfo {
if localInfo == nil {
panic("implement not found for interface IInfo, forgot register?")
}
return localInfo
}
func RegisterInfo(i IInfo) {
localInfo = i
}
func InfoRpc() IInfoRpc {
if localInfoRpc == nil {
panic("implement not found for interface IInfoRpc, forgot register?")
}
return localInfoRpc
}
func RegisterInfoRpc(i IInfoRpc) {
localInfoRpc = i
}
+40
View File
@@ -0,0 +1,40 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package xjpubservice
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
)
type (
IMiddleware interface {
// 响应处理中间件
ResponseHandler(r *ghttp.Request)
// CORS 跨域处理
CORS(r *ghttp.Request)
// IsExceptLogin 判断是否需要验证登录
IsExceptLogin(ctx context.Context, path string) bool
// ResponseHandlerRaw 返回处理中间件
ResponseHandlerRaw(r *ghttp.Request)
}
)
var (
localMiddleware IMiddleware
)
func Middleware() IMiddleware {
if localMiddleware == nil {
panic("implement not found for interface IMiddleware, forgot register?")
}
return localMiddleware
}
func RegisterMiddleware(i IMiddleware) {
localMiddleware = i
}
+55
View File
@@ -0,0 +1,55 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
)
type (
IChannelConfig interface {
Start(ctx context.Context) error
Stop(ctx context.Context) error
Loop(ctx context.Context) error
LoadAllChannelConfig(ctx context.Context) error
RefreshChannel(ctx context.Context, chanKey string) error
RemoveChannel(ctx context.Context, chanKey string) error
// get alarm cache by id
GetChannel(chanKey string) (*model.SimChannelConfigItemCache, bool)
GetAllChannel(ctx context.Context) ([]*model.SimChannelConfigItemCache, error)
}
IChannelUpdate interface {
UpdateAccessToken(ctx context.Context, input *model.UpdateAccessTokenInput) error
}
)
var (
localChannelConfig IChannelConfig
localChannelUpdate IChannelUpdate
)
func ChannelConfig() IChannelConfig {
if localChannelConfig == nil {
panic("implement not found for interface IChannelConfig, forgot register?")
}
return localChannelConfig
}
func RegisterChannelConfig(i IChannelConfig) {
localChannelConfig = i
}
func ChannelUpdate() IChannelUpdate {
if localChannelUpdate == nil {
panic("implement not found for interface IChannelUpdate, forgot register?")
}
return localChannelUpdate
}
func RegisterChannelUpdate(i IChannelUpdate) {
localChannelUpdate = i
}
+33
View File
@@ -0,0 +1,33 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
)
type (
ISimKeyFill interface {
SimKeyFill(ctx context.Context, input *model.SimBaseReqJson) (output model.SimBaseResJson, err error)
SetRedis(ctx context.Context, simInfoMap map[string]*model.SimKey)
}
)
var (
localSimKeyFill ISimKeyFill
)
func SimKeyFill() ISimKeyFill {
if localSimKeyFill == nil {
panic("implement not found for interface ISimKeyFill, forgot register?")
}
return localSimKeyFill
}
func RegisterSimKeyFill(i ISimKeyFill) {
localSimKeyFill = i
}
+44
View File
@@ -0,0 +1,44 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model/entity"
)
type (
IUpstreamAPI interface {
Start(ctx context.Context) error
Stop(ctx context.Context) error
RefreshChannel(ctx context.Context, chanKey string) error
RemoveChannel(ctx context.Context, chanKey string) error
RegisterChan(chanKey string, version string, channel *model.SimCardChannelCache)
RegisterTemp(tempKey string, channel model.SimCardTempI)
GetTemp(tempKey string) (model.SimCardTempI, bool)
GetChan(chanKey string) (*model.SimCardChannelCache, bool)
// 物联网卡
SimBase(ctx context.Context, input *model.SimBaseReqJson) (model.SimBaseResJson, error)
// 异步写入redis
SetRedis(ctx context.Context, simCardInfos []*entity.TSimCardInfo)
}
)
var (
localUpstreamAPI IUpstreamAPI
)
func UpstreamAPI() IUpstreamAPI {
if localUpstreamAPI == nil {
panic("implement not found for interface IUpstreamAPI, forgot register?")
}
return localUpstreamAPI
}
func RegisterUpstreamAPI(i IUpstreamAPI) {
localUpstreamAPI = i
}
+49
View File
@@ -0,0 +1,49 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
)
type (
ISimDX interface {
NewTemp(ctx context.Context, channelConfig *model.SimChannelConfigItemCache) model.SimCardChannelI
SimBase(ctx context.Context, input *model.SimBaseReqJson) (model.SimBaseResJson, error)
}
ISimYD interface {
NewTemp(ctx context.Context, channelConfig *model.SimChannelConfigItemCache) model.SimCardChannelI
SimBase(ctx context.Context, input *model.SimBaseReqJson) (model.SimBaseResJson, error)
}
)
var (
localSimDX ISimDX
localSimYD ISimYD
)
func SimDX() ISimDX {
if localSimDX == nil {
panic("implement not found for interface ISimDX, forgot register?")
}
return localSimDX
}
func RegisterSimDX(i ISimDX) {
localSimDX = i
}
func SimYD() ISimYD {
if localSimYD == nil {
panic("implement not found for interface ISimYD, forgot register?")
}
return localSimYD
}
func RegisterSimYD(i ISimYD) {
localSimYD = i
}