|
|
@@ -3,222 +3,165 @@ package main
|
|
3
|
3
|
import (
|
|
4
|
4
|
"encoding/json"
|
|
5
|
5
|
"log"
|
|
6
|
|
- "net/http"
|
|
7
|
|
- "strings"
|
|
8
|
|
- "time"
|
|
|
6
|
+
|
|
|
7
|
+ "git.x2erp.com/qdy/go-db/factory/database"
|
|
|
8
|
+ "git.x2erp.com/qdy/go-db/factory/mongodb"
|
|
|
9
|
+ "git.x2erp.com/qdy/go-svc-worker/internal/domain"
|
|
9
|
10
|
|
|
10
|
11
|
"git.x2erp.com/qdy/go-base/config"
|
|
|
12
|
+ "git.x2erp.com/qdy/go-base/consul"
|
|
|
13
|
+ "git.x2erp.com/qdy/go-base/container"
|
|
|
14
|
+ "git.x2erp.com/qdy/go-base/ctx"
|
|
11
|
15
|
"git.x2erp.com/qdy/go-base/logger"
|
|
12
|
|
- "git.x2erp.com/qdy/go-base/myservice"
|
|
|
16
|
+ "git.x2erp.com/qdy/go-base/middleware"
|
|
13
|
17
|
"git.x2erp.com/qdy/go-base/types"
|
|
14
|
|
- "git.x2erp.com/qdy/go-db/factory/database"
|
|
15
|
|
- "git.x2erp.com/qdy/go-svc-worker/service"
|
|
16
|
|
- "go-micro.dev/v4/metadata"
|
|
|
18
|
+ "git.x2erp.com/qdy/go-base/webx"
|
|
|
19
|
+ "git.x2erp.com/qdy/go-base/webx/health"
|
|
|
20
|
+ "git.x2erp.com/qdy/go-base/webx/router"
|
|
17
|
21
|
)
|
|
18
|
22
|
|
|
19
|
|
-// 定义业务服务
|
|
20
|
|
-type DBFactory struct {
|
|
21
|
|
- dbFactory *database.DBFactory
|
|
22
|
|
-}
|
|
23
|
|
-
|
|
24
|
|
-// 配置
|
|
25
|
23
|
var (
|
|
26
|
|
- serviceName string
|
|
27
|
|
- serviceVersion string
|
|
|
24
|
+ appName = "svc-worker"
|
|
|
25
|
+ appVersion = "1"
|
|
28
|
26
|
)
|
|
29
|
27
|
|
|
30
|
28
|
func main() {
|
|
|
29
|
+ //0.日志
|
|
|
30
|
+ //logger.InitBootLog()
|
|
|
31
|
+ logBootFactory := logger.InitBootLog()
|
|
31
|
32
|
|
|
32
|
|
- // ========== 第一阶段:强制写入文件的启动日志 ==========
|
|
33
|
|
- // 这一步确保即使配置加载失败,也有日志记录
|
|
34
|
|
- if err := logger.InitBootLog("svc-worker"); err != nil {
|
|
35
|
|
- // 连启动日志都初始化失败,只能输出到控制台
|
|
36
|
|
- log.Fatal("无法初始化启动日志: ", err)
|
|
37
|
|
- }
|
|
|
33
|
+ //1.获取配置文件
|
|
|
34
|
+ cfg := config.GetConfig()
|
|
|
35
|
+ cfg.SetAppName(appName)
|
|
|
36
|
+ cfg.SetAppVersion(appVersion)
|
|
38
|
37
|
|
|
39
|
|
- logger.BootLog("开始加载配置...")
|
|
40
|
|
- cfg, err := config.GetConfig()
|
|
41
|
|
- if err != nil {
|
|
42
|
|
- log.Fatalf("Failed to create RabbitMQ factory: %v", err)
|
|
43
|
|
- }
|
|
|
38
|
+ //2.创建关闭容器
|
|
|
39
|
+ ctr := container.NewContainer(cfg)
|
|
44
|
40
|
|
|
45
|
|
- serviceConfig := cfg.GetService()
|
|
46
|
|
- microConfig := cfg.GetMicro()
|
|
47
|
|
- serviceName = serviceConfig.ServiceName
|
|
48
|
|
- serviceVersion = serviceConfig.ServiceVersion
|
|
|
41
|
+ //注册日志,实现自动关闭
|
|
|
42
|
+ container.Reg(ctr, logBootFactory)
|
|
49
|
43
|
|
|
50
|
|
- log.Printf("serviceName: %s", serviceName)
|
|
51
|
|
- log.Printf("Port: %d", serviceConfig.Port)
|
|
52
|
|
- log.Printf("Consul: %s", microConfig.RegistryAddress)
|
|
|
44
|
+ //3.创建数据库工厂--如果需求
|
|
|
45
|
+ dbFactory := container.Create(ctr, database.CreateDBFactory)
|
|
53
|
46
|
|
|
54
|
|
- // 2. 初始化数据库
|
|
55
|
|
- dbFactory, err := database.GetDBFactory()
|
|
56
|
|
- if err != nil {
|
|
57
|
|
- log.Fatal("数据库连接失败:", err)
|
|
58
|
|
- }
|
|
59
|
|
- defer func() {
|
|
60
|
|
- if err := dbFactory.Close(); err != nil {
|
|
61
|
|
- logger.Info("数据库关闭错误: %v", err)
|
|
62
|
|
- }
|
|
63
|
|
- }()
|
|
|
47
|
+ // 赋值认证中间件参数
|
|
|
48
|
+ middleware.JWTAuthMiddlewareInit(cfg)
|
|
64
|
49
|
|
|
65
|
|
- // 3. 创建服务实例
|
|
66
|
|
- dbfactory := &DBFactory{dbFactory: dbFactory}
|
|
|
50
|
+ //测试数据库连接
|
|
|
51
|
+ dbFactory.TestConnection()
|
|
67
|
52
|
|
|
68
|
|
- // 4. 使用 micro.Start 启动服务
|
|
69
|
|
- webService := myservice.Start(cfg)
|
|
|
53
|
+ // 创建mongodb
|
|
|
54
|
+ mongoDBFactory := container.Create(ctr, mongodb.CreateFactory)
|
|
|
55
|
+ mongoDBFactory.TestConnection()
|
|
70
|
56
|
|
|
71
|
|
- // 7. 注册HTTP路由
|
|
72
|
|
- webService.Handle("/", http.HandlerFunc(rootHandler))
|
|
73
|
|
- webService.Handle("/health", http.HandlerFunc(dbfactory.healthHandler))
|
|
74
|
|
- webService.Handle("/info", http.HandlerFunc(infoHandler))
|
|
75
|
|
- webService.Handle("/api/data/agent/to/doris", authMiddleware(http.HandlerFunc(dbfactory.agentToDorisHandler)))
|
|
|
57
|
+ //得到webservice服务工厂
|
|
|
58
|
+ webxFactory := webx.GetWebServiceFactory()
|
|
76
|
59
|
|
|
77
|
|
- logger.InitRuntimeLogger(serviceName, cfg.GetLog())
|
|
|
60
|
+ //建立hhtpService服务
|
|
|
61
|
+ webServcie, _ := webxFactory.CreateService(cfg.GetServiceConfig())
|
|
78
|
62
|
|
|
79
|
|
- log.Println("日志系统初始化完成")
|
|
|
63
|
+ //建立路由-api
|
|
|
64
|
+ routerService := router.NewWebService(webServcie.GetRouter())
|
|
80
|
65
|
|
|
81
|
|
- // 程序退出时停止ES写入器
|
|
82
|
|
- defer logger.StopESWriter()
|
|
83
|
|
- logger.Debug("测试日志是否写入ES-----")
|
|
|
66
|
+ //注册路由--api
|
|
|
67
|
+ registerDefaultRouter(routerService, mongoDBFactory)
|
|
84
|
68
|
|
|
85
|
|
- //关闭-启动日志输出文件功能
|
|
86
|
|
- logger.CloseBootLogger()
|
|
|
69
|
+ // 注册健康检查-api
|
|
|
70
|
+ health.RegisterConsulHealthCheck(routerService)
|
|
87
|
71
|
|
|
88
|
|
- if err := webService.Run(); err != nil {
|
|
89
|
|
- log.Fatal("服务运行失败:", err)
|
|
90
|
|
- }
|
|
|
72
|
+ //启动服务
|
|
|
73
|
+ webServcie.Run()
|
|
91
|
74
|
|
|
92
|
|
-}
|
|
|
75
|
+ //启用运行日志
|
|
|
76
|
+ container.Create(ctr, logger.InitRuntimeLogger)
|
|
93
|
77
|
|
|
94
|
|
-// 根处理器
|
|
95
|
|
-func rootHandler(w http.ResponseWriter, r *http.Request) {
|
|
96
|
|
- if r.URL.Path != "/" {
|
|
97
|
|
- http.NotFound(w, r)
|
|
98
|
|
- return
|
|
99
|
|
- }
|
|
100
|
|
- respondJSON(w, http.StatusOK, map[string]string{
|
|
101
|
|
- "service": serviceName,
|
|
102
|
|
- "status": "running",
|
|
103
|
|
- "mode": "http-microservice",
|
|
104
|
|
- })
|
|
|
78
|
+ //注册到注册中心
|
|
|
79
|
+ container.Create(ctr, consul.Register)
|
|
|
80
|
+ //等待关闭
|
|
|
81
|
+ webServcie.WaitForServiceShutdown(ctr)
|
|
105
|
82
|
}
|
|
106
|
83
|
|
|
107
|
|
-// 健康检查处理器
|
|
108
|
|
-func (s *DBFactory) healthHandler(w http.ResponseWriter, r *http.Request) {
|
|
109
|
|
- if err := s.dbFactory.TestConnection(s.dbFactory.GetDBType()); err != nil {
|
|
110
|
|
- respondJSON(w, http.StatusServiceUnavailable, map[string]string{
|
|
111
|
|
- "status": "down",
|
|
112
|
|
- "error": err.Error(),
|
|
113
|
|
- })
|
|
114
|
|
- return
|
|
115
|
|
- }
|
|
116
|
|
- respondJSON(w, http.StatusOK, map[string]string{
|
|
117
|
|
- "status": "up",
|
|
118
|
|
- "time": time.Now().Format(time.RFC3339),
|
|
119
|
|
- })
|
|
120
|
|
-}
|
|
121
|
|
-
|
|
122
|
|
-// 信息处理器
|
|
123
|
|
-func infoHandler(w http.ResponseWriter, r *http.Request) {
|
|
124
|
|
- respondJSON(w, http.StatusOK, map[string]interface{}{
|
|
125
|
|
- "service": serviceName,
|
|
126
|
|
- "version": serviceVersion,
|
|
127
|
|
- "api": map[string]string{
|
|
128
|
|
- "POST /api/data/agent/to/doris": "同步数据到Doris",
|
|
129
|
|
- "GET /health": "健康检查",
|
|
130
|
|
- "GET /info": "服务信息",
|
|
131
|
|
- "GET /": "根路径",
|
|
132
|
|
- },
|
|
133
|
|
- "features": []string{
|
|
134
|
|
- "服务发现(Consul)",
|
|
135
|
|
- "负载均衡",
|
|
136
|
|
- "健康检查",
|
|
137
|
|
- "HTTP API网关",
|
|
|
84
|
+func registerDefaultRouter(ws *router.RouterService, mongoDBFactory *mongodb.MongoDBFactory) {
|
|
|
85
|
+ // // GET示例:路径参数绑定
|
|
|
86
|
+ // ws.GET("/api/users/{id}",
|
|
|
87
|
+ // func(id string, reqCtx *ctx.RequestContext) (UserResponse, error) {
|
|
|
88
|
+
|
|
|
89
|
+ // log.Print("ctx TenantID:", reqCtx.TenantID)
|
|
|
90
|
+ // // id 自动从路径绑定
|
|
|
91
|
+ // // 注意:webx版本没有自动注入dbFactory
|
|
|
92
|
+ // return getUser(id, dbFactory) // 需要修改getUser函数以获取dbFactory
|
|
|
93
|
+ // },
|
|
|
94
|
+ // ).Use(middleware.JWTAuthMiddleware).Register()
|
|
|
95
|
+
|
|
|
96
|
+ // POST示例:Body参数绑定
|
|
|
97
|
+ ws.POST("/api/tenant/config",
|
|
|
98
|
+ func(req domain.TenantConfig, reqCtx *ctx.RequestContext) (*types.QueryResult[interface{}], error) {
|
|
|
99
|
+ log.Print("ctx TenantID:", reqCtx.TenantID)
|
|
|
100
|
+ //log.Print("mongoDBFactory:", mongoDBFactory.GetConfig().URI)
|
|
|
101
|
+ //log.Print("dbFactory:", dbFactory.GetDBName())
|
|
|
102
|
+ jsonBytes, _ := json.Marshal(req)
|
|
|
103
|
+ log.Printf("TenantConfig :%s", string(jsonBytes))
|
|
|
104
|
+
|
|
|
105
|
+ ok := mongoDBFactory.InsertOne("tenant_configs", req)
|
|
|
106
|
+ log.Print("TenantConfig InsertOne:", ok)
|
|
|
107
|
+ // req 自动从JSON Body绑定
|
|
|
108
|
+ return &types.QueryResult[interface{}]{
|
|
|
109
|
+ Success: ok,
|
|
|
110
|
+ Data: mongoDBFactory.GetConfig().URI,
|
|
|
111
|
+ }, nil
|
|
138
|
112
|
},
|
|
139
|
|
- })
|
|
140
|
|
-}
|
|
141
|
|
-
|
|
142
|
|
-// AgentToDoris处理器
|
|
143
|
|
-func (s *DBFactory) agentToDorisHandler(w http.ResponseWriter, r *http.Request) {
|
|
144
|
|
- if r.Method != "POST" {
|
|
145
|
|
- respondJSON(w, http.StatusMethodNotAllowed, types.QueryResult{
|
|
146
|
|
- Error: "只支持POST请求",
|
|
147
|
|
- Success: false,
|
|
148
|
|
- })
|
|
149
|
|
- return
|
|
150
|
|
- }
|
|
|
113
|
+ ).Use(middleware.JWTAuthMiddleware).Register()
|
|
151
|
114
|
|
|
152
|
|
- // 解析请求
|
|
153
|
|
- var requestData types.QueryRequest
|
|
154
|
|
- if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil {
|
|
155
|
|
- respondJSON(w, http.StatusBadRequest, map[string]string{
|
|
156
|
|
- "error": "无效的JSON数据",
|
|
157
|
|
- })
|
|
158
|
|
- return
|
|
159
|
|
- }
|
|
|
115
|
+ // 您的业务路由
|
|
|
116
|
+ ws.POST("/api/query/yaml",
|
|
|
117
|
+ func() (interface{}, error) {
|
|
|
118
|
+ return queryYaml(nil) // 需要修改queryYaml函数以获取dbFactory
|
|
|
119
|
+ },
|
|
|
120
|
+ ).Use(middleware.JWTAuthMiddleware).Register()
|
|
160
|
121
|
|
|
161
|
|
- // 处理业务逻辑
|
|
162
|
|
- result := service.ServiceAgentToDoris(s.dbFactory, requestData)
|
|
|
122
|
+ ws.POST("/api/init/config/template",
|
|
|
123
|
+ func() (interface{}, error) {
|
|
|
124
|
+ return initConfigTemplate(nil) // 需要修改initConfigTemplate函数以获取dbFactory
|
|
|
125
|
+ },
|
|
|
126
|
+ ).Use(middleware.JWTAuthMiddleware).Register()
|
|
163
|
127
|
|
|
164
|
|
- respondJSON(w, http.StatusOK, result)
|
|
165
|
128
|
}
|
|
166
|
129
|
|
|
167
|
|
-// 认证中间件
|
|
168
|
|
-func authMiddleware(next http.Handler) http.Handler {
|
|
169
|
|
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
170
|
|
-
|
|
171
|
|
- // JWT令牌认证
|
|
172
|
|
- token := r.Header.Get("Authorization")
|
|
173
|
|
- if token != "" && strings.HasPrefix(token, "Bearer ") {
|
|
174
|
|
- token = token[7:]
|
|
175
|
|
- }
|
|
176
|
|
-
|
|
177
|
|
- // 双重认证:API密钥或JWT
|
|
178
|
|
- if token == "" {
|
|
179
|
|
- respondJSON(w, http.StatusUnauthorized, map[string]string{
|
|
180
|
|
- "error": "需要API密钥或Bearer令牌",
|
|
181
|
|
- })
|
|
182
|
|
- return
|
|
183
|
|
- }
|
|
184
|
|
-
|
|
185
|
|
- // 验证JWT令牌
|
|
186
|
|
- if token != "" && !isValidJWT(token) {
|
|
187
|
|
- respondJSON(w, http.StatusUnauthorized, map[string]string{
|
|
188
|
|
- "error": "无效的访问令牌",
|
|
189
|
|
- })
|
|
190
|
|
- return
|
|
191
|
|
- }
|
|
192
|
|
-
|
|
193
|
|
- // 将认证信息添加到上下文
|
|
194
|
|
- ctx := r.Context()
|
|
195
|
|
- if token != "" {
|
|
196
|
|
- ctx = metadata.Set(ctx, "Authorization", "Bearer "+token)
|
|
197
|
|
- }
|
|
198
|
|
-
|
|
199
|
|
- next.ServeHTTP(w, r.WithContext(ctx))
|
|
200
|
|
- })
|
|
|
130
|
+func queryYaml(dbFactory *database.DBFactory) (interface{}, error) {
|
|
|
131
|
+ // 您的业务逻辑
|
|
|
132
|
+ return map[string]interface{}{"message": "query yaml success"}, nil
|
|
201
|
133
|
}
|
|
202
|
134
|
|
|
203
|
|
-// JWT验证
|
|
204
|
|
-func isValidJWT(token string) bool {
|
|
205
|
|
- // TODO: 实现JWT验证逻辑
|
|
206
|
|
- // 可以使用 github.com/golang-jwt/jwt/v5
|
|
207
|
|
- // 临时实现:检查token是否有效格式
|
|
208
|
|
- //if len(token) < 10 {
|
|
209
|
|
- // return false
|
|
210
|
|
- // }
|
|
211
|
|
- return true // 临时返回true,实际需要验证签名和过期时间
|
|
|
135
|
+func initConfigTemplate(dbFactory *database.DBFactory) (interface{}, error) {
|
|
|
136
|
+ // 您的业务逻辑
|
|
|
137
|
+ return map[string]interface{}{"message": "init config success"}, nil
|
|
212
|
138
|
}
|
|
213
|
139
|
|
|
214
|
|
-// JSON响应辅助函数
|
|
215
|
|
-func respondJSON(w http.ResponseWriter, status int, data interface{}) {
|
|
216
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
217
|
|
- w.Header().Set("X-Service-Name", serviceName)
|
|
218
|
|
- w.Header().Set("X-Service-Version", serviceVersion)
|
|
219
|
|
- w.WriteHeader(status)
|
|
|
140
|
+// getSQLWithPagination 生成带分页的SQL语句(参数模式)
|
|
|
141
|
+// 返回SQL语句和参数映射
|
|
|
142
|
+func getSQLWithPaginationSQL(startRow, endRow int) (string, []interface{}) {
|
|
|
143
|
+ sql := `SELECT
|
|
|
144
|
+ CLOTHING_ID,
|
|
|
145
|
+ CLOTHING_YEAR,
|
|
|
146
|
+ CLOTHING_NAME
|
|
|
147
|
+
|
|
|
148
|
+ FROM (
|
|
|
149
|
+ SELECT a.*, ROWNUM as rn
|
|
|
150
|
+ FROM (
|
|
|
151
|
+ SELECT *
|
|
|
152
|
+ FROM X6_STOCK_DEV.A3_CLOTHING
|
|
|
153
|
+
|
|
|
154
|
+ ORDER BY CLOTHING_ID
|
|
|
155
|
+ ) a
|
|
|
156
|
+ WHERE ROWNUM <= :1
|
|
|
157
|
+)
|
|
|
158
|
+WHERE rn > :2`
|
|
220
|
159
|
|
|
221
|
|
- if err := json.NewEncoder(w).Encode(data); err != nil {
|
|
222
|
|
- log.Printf("JSON编码错误: %v", err)
|
|
|
160
|
+ // 创建参数映射
|
|
|
161
|
+ params := []interface{}{
|
|
|
162
|
+ endRow,
|
|
|
163
|
+ startRow - 1, // WHERE rn > :start_row 所以是startRow-1
|
|
223
|
164
|
}
|
|
|
165
|
+
|
|
|
166
|
+ return sql, params
|
|
224
|
167
|
}
|