Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ddc2f17b9 | |||
| 91359f61ac | |||
| e1829b90bf | |||
| 6980b31da7 | |||
| 4960021748 | |||
| 960dca6a50 | |||
| 518d45d296 |
@@ -251,7 +251,7 @@ func GetInstanceAddr(ctx context.Context, name string) (addr string, err error)
|
||||
err = errors.New("获取服务监听器失败")
|
||||
return
|
||||
}
|
||||
|
||||
defer watch.Close()
|
||||
service, err := watch.Proceed()
|
||||
if err != nil || service == nil {
|
||||
err = errors.New("获取服务实例失败")
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
@@ -27,9 +28,30 @@ import (
|
||||
// ==================== 缓存管理器(单例) ====================
|
||||
|
||||
var (
|
||||
localCache *gcache.Cache
|
||||
localCache *gcache.Cache
|
||||
snowflakeNode *snowflake.Node
|
||||
snowflakeOnce sync.Once
|
||||
)
|
||||
|
||||
func init() {
|
||||
ctx := context.Background()
|
||||
snowflakeOnce.Do(func() {
|
||||
nodeId := genv.Get("APP_NODE", 1).Int64()
|
||||
// 安全范围 0~1023
|
||||
if nodeId < 0 || nodeId > 1023 {
|
||||
nodeId = 1
|
||||
}
|
||||
|
||||
node, err := snowflake.NewNode(nodeId)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "snowflake init failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
snowflakeNode = node
|
||||
})
|
||||
}
|
||||
|
||||
// getLocalCache 获取本地缓存实例
|
||||
func getLocalCache() *gcache.Cache {
|
||||
if localCache == nil {
|
||||
@@ -165,18 +187,13 @@ func insertHook(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeId := genv.Get("APP_NODE", "").Int64()
|
||||
if g.IsEmpty(nodeId) {
|
||||
nodeId = 1
|
||||
if g.IsEmpty(snowflakeNode) {
|
||||
return nil, fmt.Errorf("snowflakeNode is nil")
|
||||
}
|
||||
|
||||
node, err := snowflake.NewNode(nodeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range in.Data {
|
||||
if _, ok := in.Data[i]["id"]; ok {
|
||||
in.Data[i]["id"] = node.Generate().Int64()
|
||||
in.Data[i]["id"] = snowflakeNode.Generate().Int64()
|
||||
}
|
||||
if _, ok := in.Data[i]["tenant_id"]; ok {
|
||||
if g.IsEmpty(in.Data[i]["tenant_id"]) {
|
||||
@@ -188,7 +205,7 @@ func insertHook(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result
|
||||
}
|
||||
}
|
||||
if _, ok := in.Data[i]["creator"]; ok {
|
||||
if g.IsEmpty(in.Data[i]["tenant_id"]) {
|
||||
if g.IsEmpty(in.Data[i]["creator"]) {
|
||||
if !g.IsEmpty(userInfo.UserName) {
|
||||
in.Data[i]["creator"] = userInfo.UserName
|
||||
} else {
|
||||
|
||||
45
http/http.go
45
http/http.go
@@ -2,6 +2,7 @@ package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
@@ -129,28 +130,28 @@ func doRequest(ctx context.Context, method string, url string, headers map[strin
|
||||
return
|
||||
}
|
||||
defer response.Close()
|
||||
//result := response.ReadAll()
|
||||
//
|
||||
//// 统一处理内部API响应格式:{code:200,message:"",data:{...}}
|
||||
//resultStrut := &ghttp.DefaultHandlerResponse{}
|
||||
//
|
||||
//if err = gconv.Struct(result, &resultStrut); err != nil { // 修复:增加err检查
|
||||
// return errors.New("响应解析失败: " + err.Error())
|
||||
//}
|
||||
//
|
||||
//// 添加调试日志:打印解析后的结构
|
||||
//g.Log().Debugf(ctx, "[HTTP] 解析后结构: Code=%d, Message=%s, Data类型=%T, Data值=%+v",
|
||||
// resultStrut.Code, resultStrut.Message, resultStrut.Data, resultStrut.Data)
|
||||
//
|
||||
//if resultStrut.Code == 200 || resultStrut.Code == 0 {
|
||||
// if err = gconv.Struct(resultStrut.Data, target); err != nil { // 修复:增加err检查
|
||||
// return errors.New("数据解析失败: " + err.Error())
|
||||
// }
|
||||
// // 添加调试日志:打印最终的target
|
||||
// g.Log().Debugf(ctx, "[HTTP] 最终target: %+v", target)
|
||||
//} else {
|
||||
// err = errors.New(resultStrut.Message)
|
||||
//}
|
||||
result := response.ReadAll()
|
||||
|
||||
// 统一处理内部API响应格式:{code:200,message:"",data:{...}}
|
||||
resultStrut := &ghttp.DefaultHandlerResponse{}
|
||||
|
||||
if err = gconv.Struct(result, &resultStrut); err != nil { // 修复:增加err检查
|
||||
return errors.New("响应解析失败: " + err.Error())
|
||||
}
|
||||
|
||||
// 添加调试日志:打印解析后的结构
|
||||
g.Log().Debugf(ctx, "[HTTP] 解析后结构: Code=%d, Message=%s, Data类型=%T, Data值=%+v",
|
||||
resultStrut.Code, resultStrut.Message, resultStrut.Data, resultStrut.Data)
|
||||
|
||||
if resultStrut.Code == 200 || resultStrut.Code == 0 {
|
||||
if err = gconv.Struct(resultStrut.Data, target); err != nil { // 修复:增加err检查
|
||||
return errors.New("数据解析失败: " + err.Error())
|
||||
}
|
||||
// 添加调试日志:打印最终的target
|
||||
g.Log().Debugf(ctx, "[HTTP] 最终target: %+v", target)
|
||||
} else {
|
||||
err = errors.New(resultStrut.Message)
|
||||
}
|
||||
return
|
||||
}
|
||||
func Get(ctx context.Context, url string, headers map[string]string, target any, data ...any) (err error) {
|
||||
|
||||
@@ -92,7 +92,7 @@ func UserLimiter(r *ghttp.Request) {
|
||||
var userName string
|
||||
user, err := utils.GetUserInfo(r.GetCtx())
|
||||
if err != nil {
|
||||
r.Response.WriteStatusExit(429, err.Error())
|
||||
r.Response.WriteStatusExit(401, err.Error())
|
||||
return
|
||||
}
|
||||
userName = gconv.String(user.UserName)
|
||||
|
||||
44
utils/gse.go
44
utils/gse.go
@@ -2,6 +2,8 @@ package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
@@ -54,7 +56,24 @@ func newGseTool() (tool *gseTool, err error) {
|
||||
// 2. 初始化 TF-IDF 提取器
|
||||
tfidf := &extracker.TagExtracter{}
|
||||
tfidf.WithGse(seg)
|
||||
err = tfidf.LoadIdf()
|
||||
|
||||
// 尝试从默认路径加载 IDF 字典
|
||||
idfPath := getIdfDictPath()
|
||||
if idfPath != "" {
|
||||
// 如果找到自定义路径,使用 LoadDict 方法加载
|
||||
err = tfidf.LoadDict(idfPath)
|
||||
if err != nil {
|
||||
glog.Warningf(context.Background(), "加载自定义 IDF 字典失败 [%s]: %v,将使用默认字典", idfPath, err)
|
||||
// 回退到默认加载方式
|
||||
err = tfidf.LoadIdf()
|
||||
} else {
|
||||
glog.Infof(context.Background(), "成功加载自定义 IDF 字典: %s", idfPath)
|
||||
}
|
||||
} else {
|
||||
// 使用默认的 IDF 字典
|
||||
err = tfidf.LoadIdf()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -71,6 +90,29 @@ func newGseTool() (tool *gseTool, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// getIdfDictPath 获取 IDF 字典文件路径
|
||||
func getIdfDictPath() string {
|
||||
// 1. 尝试从容器内的默认挂载路径加载(Docker 卷映射)
|
||||
containerPath := "/app/dict/zh/idf.txt"
|
||||
if _, err := os.Stat(containerPath); err == nil {
|
||||
return containerPath
|
||||
}
|
||||
|
||||
// 2. 尝试从当前工作目录的 dict/zh/idf.txt 加载
|
||||
workDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
localPath := filepath.Join(workDir, "dict", "zh", "idf.txt")
|
||||
if _, err := os.Stat(localPath); err == nil {
|
||||
return localPath
|
||||
}
|
||||
|
||||
// 3. 如果没有找到自定义路径,返回空字符串,使用默认字典
|
||||
return ""
|
||||
}
|
||||
|
||||
// Cut 分词(关键词提取唯一正确模式:精确模式 + HMM)
|
||||
func (k *gseTool) Cut(text string) []string {
|
||||
return k.seg.Cut(text, true)
|
||||
|
||||
130
utils/utils.go
130
utils/utils.go
@@ -460,3 +460,133 @@ func IsLocalIP(ip string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetLocalIP 获取本机有效的局域网 IPv4 地址
|
||||
func GetLocalIP() string {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return "127.0.0.1"
|
||||
}
|
||||
|
||||
var validIPs []string
|
||||
|
||||
for _, addr := range addrs {
|
||||
ipnet, ok := addr.(*net.IPNet)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
ip := ipnet.IP
|
||||
|
||||
if isIPValid(ip) {
|
||||
validIPs = append(validIPs, ip.String())
|
||||
}
|
||||
}
|
||||
|
||||
// 优先返回非 169.254.x.x 的 IP
|
||||
for _, ip := range validIPs {
|
||||
if !strings.HasPrefix(ip, "169.254.") {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
|
||||
// 其次返回 169.254.x.x(最后的选择)
|
||||
if len(validIPs) > 0 {
|
||||
return validIPs[0]
|
||||
}
|
||||
|
||||
return "127.0.0.1"
|
||||
}
|
||||
|
||||
// isIPValid 判断 IP 是否有效
|
||||
func isIPValid(ip net.IP) bool {
|
||||
// 不是 loopback (127.0.0.1)
|
||||
if ip.IsLoopback() {
|
||||
return false
|
||||
}
|
||||
|
||||
// 是 IPv4
|
||||
if ip.To4() == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 不是链路本地地址 (169.254.0.0/16)
|
||||
if ip[0] == 169 && ip[1] == 254 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 不是组播地址
|
||||
if ip.IsMulticast() {
|
||||
return false
|
||||
}
|
||||
|
||||
// 不是未指定地址 (0.0.0.0)
|
||||
if ip.IsUnspecified() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func GetServerPort(ctx context.Context) string {
|
||||
address := g.Cfg().MustGet(ctx, "server.address", ":8080").String()
|
||||
// address 格式如 ":3009",去掉冒号
|
||||
if strings.HasPrefix(address, ":") {
|
||||
return address[1:]
|
||||
}
|
||||
return "8080"
|
||||
}
|
||||
|
||||
// GetLocalAddress 获取局域网地址(IP:端口)
|
||||
func GetLocalAddress(ctx context.Context) string {
|
||||
ip := GetLocalIP()
|
||||
port := GetServerPort(ctx)
|
||||
|
||||
if port == "80" || port == "443" {
|
||||
return ip
|
||||
}
|
||||
return ip + ":" + port
|
||||
}
|
||||
|
||||
// GetSchemaFromRequest 从当前请求中获取协议(http/https)
|
||||
func GetSchemaFromRequest(ctx context.Context) string {
|
||||
r := g.RequestFromCtx(ctx)
|
||||
if r == nil {
|
||||
return "http"
|
||||
}
|
||||
|
||||
// 1. 代理场景:X-Forwarded-Proto
|
||||
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
|
||||
return proto
|
||||
}
|
||||
|
||||
// 2. 代理场景:X-Forwarded-Scheme
|
||||
if proto := r.Header.Get("X-Forwarded-Scheme"); proto != "" {
|
||||
return proto
|
||||
}
|
||||
|
||||
// 3. TLS 连接(直接 HTTPS)
|
||||
if r.TLS != nil {
|
||||
return "https"
|
||||
}
|
||||
|
||||
// 4. 默认 HTTP(这行很重要!)
|
||||
return "http" // ← 确保有这行
|
||||
}
|
||||
|
||||
// GetLocalBaseURL 获取局域网基础 URL(动态协议 + IP + 端口)
|
||||
func GetLocalBaseURL(ctx context.Context) string {
|
||||
schema := GetSchemaFromRequest(ctx)
|
||||
addr := GetLocalAddress(ctx)
|
||||
return schema + "://" + addr
|
||||
}
|
||||
|
||||
// GetCallbackURL 获取回调地址(完整 URL)
|
||||
func GetCallbackURL(ctx context.Context, path string) string {
|
||||
baseURL := GetLocalBaseURL(ctx)
|
||||
// 确保 path 以 / 开头
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
return baseURL + path
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user