This commit is contained in:
2026-08-10 11:16:02 +08:00
parent f994b2de9c
commit 007a7ba38a
26 changed files with 469 additions and 142 deletions

View File

@@ -2,7 +2,6 @@ package main
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
@@ -12,90 +11,18 @@ import (
"bsm/full/all/internal/config"
"bsm/full/all/internal/impl"
allserver "bsm/full/all/internal/server"
ads "bsm/full/module/base/ads/service"
cloud "bsm/full/module/base/cloud/service"
cms "bsm/full/module/base/cms/service"
feedback "bsm/full/module/base/feedback/service"
fts "bsm/full/module/base/fts/service"
initial "bsm/full/module/base/initial/service"
logs "bsm/full/module/base/logs/service"
mgt "bsm/full/module/base/mgt/service"
passport "bsm/full/module/base/passport/service"
sender "bsm/full/module/base/sender/service"
address "bsm/full/module/ec/address/service"
mall "bsm/full/module/ec/mall/service"
market "bsm/full/module/ec/market/service"
order "bsm/full/module/ec/order/service"
wallet "bsm/full/module/finance/wallet/service"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"google.golang.org/grpc"
"bsm/full/all/internal/service"
)
func main() {
config.New()
impl.NewImpl()
srv := allserver.New()
srv.HTTP.Use(sessions.Sessions("mysession", cookie.NewStore([]byte(config.Spec.SecretKey+"-session"))))
exposeGRPC := func(name string, expose func(*grpc.Server) error) {
if !config.Enabled(name) {
return
}
if err := expose(srv.GRPC); err != nil {
panic(fmt.Errorf("expose %s: %w", name, err))
}
if err := service.Expose(srv); err != nil {
panic(err)
}
exposeGRPC("ads", func(g *grpc.Server) error {
return ads.Expose(ads.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("cloud", func(g *grpc.Server) error {
return cloud.Expose(cloud.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("cms", func(g *grpc.Server) error {
return cms.Expose(cms.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("feedback", func(g *grpc.Server) error {
return feedback.Expose(feedback.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("initial", func(g *grpc.Server) error {
return initial.Expose(initial.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("passport", func(g *grpc.Server) error {
return passport.Expose(passport.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("sender", func(g *grpc.Server) error {
return sender.Expose(sender.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("address", func(g *grpc.Server) error {
return address.Expose(address.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("mall", func(g *grpc.Server) error {
return mall.Expose(mall.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("market", func(g *grpc.Server) error {
return market.Expose(market.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("order", func(g *grpc.Server) error {
return order.Expose(order.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeGRPC("wallet", func(g *grpc.Server) error {
return wallet.Expose(wallet.ExposeOptions{GRPC: g, Gateway: srv.Gateway})
})
exposeHTTP := func(name string, expose func() error) {
if !config.Enabled(name) {
return
}
if err := expose(); err != nil {
panic(fmt.Errorf("expose %s: %w", name, err))
}
}
exposeHTTP("fts", func() error { return fts.Expose(srv.HTTP) })
exposeHTTP("logs", func() error { return logs.Expose(srv.HTTP) })
exposeHTTP("mgt", func() error { return mgt.Expose(srv.HTTP) })
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
go func() {
@@ -104,6 +31,7 @@ func main() {
defer cancel()
_ = srv.Stop(ctx)
}()
if err := srv.Start(config.Spec.Addr); err != nil && err != http.ErrServerClosed {
panic(err)
}

View File

@@ -10,54 +10,103 @@ Databases:
Cache: redis://null:CHANGE_ME@127.0.0.1:6379/
SecretKey: CHANGE_ME
# The shared listener serves gRPC over HTTP/2 and HTTP/Gateway routes over
# HTTP/1.1 or h2c. Per-service values override the shared settings above.
# Enabled services. Use "all" to enable every registered service.
Services:
ads:
Enable: true
cloud:
Enable: true
cms:
Enable: true
feedback:
Enable: true
fts:
Enable: true
initial:
Enable: true
logs:
Enable: true
mgt:
Enable: true
passport:
Enable: true
Token:
Prefix: /token/
Expire: 86400
Kyc:
Provider: jumio
BaseUrl: CHANGE_ME
ApiSecret: CHANGE_ME
ApiToken: CHANGE_ME
ApiArgs: CHANGE_ME
WeChatConf:
AppID: CHANGE_ME
AppSecret: CHANGE_ME
sender:
Enable: true
address:
Enable: true
mall:
Enable: true
market:
Enable: true
order:
Enable: true
wallet:
Enable: true
Wallet:
Name: default
URL: CHANGE_ME
AlipyIsOpen: false
WalletIsOpen: false
WechatpayIsOpen: false
- ads
- cloud
- cms
- feedback
- fts
- initial
- logs
- mgt
- passport
- sender
- address
- mall
- market
- order
- wallet
# Service-specific configuration is flat at the document root. Shared
# database, Redis, RPC, APM and Etcd settings above are assigned automatically.
Fts:
MinioOss:
Site: http://127.0.0.1:9000
Endpoint: 127.0.0.1:9000
AccessKeyId: CHANGE_ME
AccessKeySecret: CHANGE_ME
UseSSL: false
Local:
Site: http://127.0.0.1:12000/files
UploadDir: ./uploader/
FtsConfig:
InputKey: file
MaxSize: 5368709120
Allows: [.doc, .docx, .txt, .png, .jpg, .jpeg, .gif, .pdf, .xls, .xlsx, .ppt, .pptx, .mp4, .avi, .mkv, .webp, .mov, .wmv, .mp3, .wav]
Mgt:
InitRootUser: true
SecretKey: CHANGE_ME
Passport:
Token:
Prefix: /token/
Expire: 86400
Kyc:
Provider: jumio
BaseUrl: CHANGE_ME
ApiSecret: CHANGE_ME
ApiToken: CHANGE_ME
ApiArgs: CHANGE_ME
WeChatConf:
AppID: CHANGE_ME
AppSecret: CHANGE_ME
Sender:
SMTP:
default:
Endpoint: smtp.example.com
Port: 465
Username: CHANGE_ME
Password: CHANGE_ME
FromAddress: no-reply@example.com
FromName: BSM
SMS:
default:
Endpoint: CHANGE_ME
AccessKeyId: CHANGE_ME
AccessKeySecret: CHANGE_ME
Region: CHANGE_ME
Code:
Length: 6
Expire: 300
MaxSentLimit: 5
CokeyKey: code
BlackListFilter: [127.0.0.1]
Wallet:
QrCodeSavePath: ./qrcode/
WeChat:
AppID: CHANGE_ME
AppSecret: CHANGE_ME
SerialNo: CHANGE_ME
APIV3Key: CHANGE_ME
PrivateKey: CHANGE_ME
NotifyURL: CHANGE_ME
Currency: CNY
MchID: CHANGE_ME
Alipay:
AppID: CHANGE_ME
AppSecret: CHANGE_ME
PrivateKey: CHANGE_ME
IsProd: false
NotifyURL: CHANGE_ME
ReturnURL: CHANGE_ME
QuitURL: CHANGE_ME
Wallet:
Name: default
URL: CHANGE_ME
AlipyIsOpen: false
WalletIsOpen: false
WechatpayIsOpen: false

View File

@@ -5,18 +5,29 @@ import (
"os"
"strings"
ftsService "bsm/full/module/base/fts/service"
mgtService "bsm/full/module/base/mgt/service"
passportService "bsm/full/module/base/passport/service"
senderService "bsm/full/module/base/sender/service"
walletService "bsm/full/module/finance/wallet/service"
"git.apinb.com/bsm-sdk/core/conf"
)
const ServiceKey = "all"
type ServiceConfig map[string]any
type SrvConfig struct {
conf.Base `yaml:",inline"`
Databases *conf.DBConf `yaml:"Databases"`
Etcd *conf.EtcdConf `yaml:"Etcd"`
Services map[string]ServiceConfig `yaml:"Services"`
conf.Base `yaml:",inline"`
Databases *conf.DBConf `yaml:"Databases"`
MicroService *conf.MicroServiceConf `yaml:"MicroService"`
Rpc map[string]conf.RpcConf `yaml:"Rpc"`
Apm *conf.ApmConf `yaml:"APM"`
Etcd *conf.EtcdConf `yaml:"Etcd"`
Services []string `yaml:"Services"`
Fts *ftsService.Config `yaml:"Fts"`
Mgt *mgtService.Config `yaml:"Mgt"`
Passport *passportService.Config `yaml:"Passport"`
Sender *senderService.Config `yaml:"Sender"`
Wallet *walletService.Config `yaml:"Wallet"`
}
var Spec SrvConfig
@@ -27,9 +38,28 @@ func New() {
Spec.BindIP = conf.CheckIP(Spec.BindIP)
Spec.Addr = net.JoinHostPort(Spec.BindIP, Spec.Port)
conf.NotNil(Spec.Service, Spec.Cache)
assignSharedConfig()
conf.PrintInfo(Spec.Addr)
}
func assignSharedConfig() {
if Spec.Fts != nil {
Spec.Fts.Base, Spec.Fts.Databases, Spec.Fts.Rpc, Spec.Fts.Apm, Spec.Fts.Etcd = Spec.Base, Spec.Databases, Spec.Rpc, Spec.Apm, Spec.Etcd
}
if Spec.Mgt != nil {
Spec.Mgt.Base, Spec.Mgt.Databases, Spec.Mgt.Rpc, Spec.Mgt.Apm, Spec.Mgt.Etcd = Spec.Base, Spec.Databases, Spec.Rpc, Spec.Apm, Spec.Etcd
}
if Spec.Passport != nil {
Spec.Passport.Base, Spec.Passport.Databases, Spec.Passport.MicroService, Spec.Passport.Rpc, Spec.Passport.Apm, Spec.Passport.Etcd = Spec.Base, Spec.Databases, Spec.MicroService, Spec.Rpc, Spec.Apm, Spec.Etcd
}
if Spec.Sender != nil {
Spec.Sender.Base, Spec.Sender.Databases, Spec.Sender.MicroService, Spec.Sender.Rpc, Spec.Sender.Apm, Spec.Sender.Etcd = Spec.Base, Spec.Databases, Spec.MicroService, Spec.Rpc, Spec.Apm, Spec.Etcd
}
if Spec.Wallet != nil {
Spec.Wallet.Base, Spec.Wallet.Databases, Spec.Wallet.MicroService, Spec.Wallet.Rpc, Spec.Wallet.Apm, Spec.Wallet.Etcd = Spec.Base, Spec.Databases, Spec.MicroService, Spec.Rpc, Spec.Apm, Spec.Etcd
}
}
func Enabled(name string) bool {
if raw := strings.TrimSpace(os.Getenv("BSM_SERVICES")); raw != "" {
for _, selected := range strings.Split(raw, ",") {
@@ -39,10 +69,10 @@ func Enabled(name string) bool {
}
return false
}
service, ok := Spec.Services[name]
if !ok {
return false
for _, service := range Spec.Services {
if strings.EqualFold(strings.TrimSpace(service), name) || strings.EqualFold(strings.TrimSpace(service), "all") {
return true
}
}
enabled, ok := service["Enable"].(bool)
return !ok || enabled
return false
}

View File

@@ -0,0 +1,26 @@
package config
import (
"os"
"path/filepath"
"testing"
"go.yaml.in/yaml/v3"
)
func TestAllDevConfig(t *testing.T) {
data, err := os.ReadFile(filepath.Join("..", "..", "etc", "all_dev.yaml"))
if err != nil {
t.Fatal(err)
}
var cfg SrvConfig
if err := yaml.Unmarshal(data, &cfg); err != nil {
t.Fatal(err)
}
if len(cfg.Services) == 0 {
t.Fatal("services must not be empty")
}
if cfg.Fts == nil || cfg.Mgt == nil || cfg.Passport == nil || cfg.Sender == nil || cfg.Wallet == nil {
t.Fatal("service-specific configuration is incomplete")
}
}

View File

@@ -2,20 +2,41 @@ package impl
import (
"bsm/full/all/internal/config"
"git.apinb.com/bsm-sdk/core/cache/redis"
"git.apinb.com/bsm-sdk/core/with"
"github.com/patrickmn/go-cache"
clientv3 "go.etcd.io/etcd/client/v3"
"gorm.io/gorm"
)
var (
// RedisService Redis缓存服务客户端用于存储临时数据和缓存热点数据
RedisService *redis.RedisClient
EtcdService *clientv3.Client
DBService *gorm.DB
// EtcdService Etcd客户端用于服务发现和配置管理
EtcdService *clientv3.Client
// DBService 数据库服务连接, 用于数据持久化存储
DBService *gorm.DB
// MemoryService 内存缓存服务, 用于存储频繁访问的数据,提供最快的访问速度
MemoryService *cache.Cache
)
// NewImpl 初始化实现层
// 建立与各种外部服务的连接包括内存缓存、Redis、数据库和Etcd
// 这些连接将在整个服务生命周期中使用
func NewImpl() {
// 初始化内存缓存服务,用于存储热点数据
MemoryService = with.Memory(nil)
// 初始化Redis缓存服务用于分布式缓存
RedisService = with.RedisCache(config.Spec.Cache)
// 初始化数据库连接,用于数据持久化
DBService = with.Databases(config.Spec.Databases, nil)
// 初始化Etcd客户端用于服务发现和配置管理
EtcdService = with.Etcd(config.Spec.Etcd)
}

View File

@@ -0,0 +1,13 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/ec/address/service"
)
func exposeAddress(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
})
}

View File

@@ -0,0 +1,13 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/ads/service"
)
func exposeAds(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
})
}

View File

@@ -0,0 +1,13 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/cloud/service"
)
func exposeCloud(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
})
}

View File

@@ -0,0 +1,13 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/cms/service"
)
func exposeCMS(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
})
}

View File

@@ -0,0 +1,13 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/feedback/service"
)
func exposeFeedback(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
})
}

View File

@@ -0,0 +1,11 @@
package service
import (
"bsm/full/all/internal/config"
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/fts/service"
)
func exposeFTS(srv *server.Server) error {
return moduleService.Expose(srv.HTTP, config.Spec.Fts)
}

View File

@@ -0,0 +1,13 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/initial/service"
)
func exposeInitial(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
})
}

View File

@@ -0,0 +1,10 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/logs/service"
)
func exposeLogs(srv *server.Server) error {
return moduleService.Expose(srv.HTTP)
}

View File

@@ -0,0 +1,13 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/ec/mall/service"
)
func exposeMall(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
})
}

View File

@@ -0,0 +1,13 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/ec/market/service"
)
func exposeMarket(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
})
}

View File

@@ -0,0 +1,11 @@
package service
import (
"bsm/full/all/internal/config"
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/mgt/service"
)
func exposeMgt(srv *server.Server) error {
return moduleService.Expose(srv.HTTP, config.Spec.Mgt)
}

View File

@@ -0,0 +1,13 @@
package service
import (
"bsm/full/all/internal/server"
moduleService "bsm/full/module/ec/order/service"
)
func exposeOrder(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
})
}

View File

@@ -0,0 +1,15 @@
package service
import (
"bsm/full/all/internal/config"
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/passport/service"
)
func exposePassport(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
Config: config.Spec.Passport,
})
}

View File

@@ -0,0 +1,15 @@
package service
import (
"bsm/full/all/internal/config"
"bsm/full/all/internal/server"
moduleService "bsm/full/module/base/sender/service"
)
func exposeSender(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
Config: config.Spec.Sender,
})
}

View File

@@ -0,0 +1,46 @@
package service
import (
"fmt"
"bsm/full/all/internal/config"
"bsm/full/all/internal/server"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
)
type exposeFunc func(*server.Server) error
var services = []struct {
name string
expose exposeFunc
}{
{"ads", exposeAds},
{"cloud", exposeCloud},
{"cms", exposeCMS},
{"feedback", exposeFeedback},
{"fts", exposeFTS},
{"initial", exposeInitial},
{"logs", exposeLogs},
{"mgt", exposeMgt},
{"passport", exposePassport},
{"sender", exposeSender},
{"address", exposeAddress},
{"mall", exposeMall},
{"market", exposeMarket},
{"order", exposeOrder},
{"wallet", exposeWallet},
}
func Expose(srv *server.Server) error {
srv.HTTP.Use(sessions.Sessions("mysession", cookie.NewStore([]byte(config.Spec.SecretKey+"-session"))))
for _, item := range services {
if !config.Enabled(item.name) {
continue
}
if err := item.expose(srv); err != nil {
return fmt.Errorf("expose %s: %w", item.name, err)
}
}
return nil
}

View File

@@ -0,0 +1,15 @@
package service
import (
"bsm/full/all/internal/config"
"bsm/full/all/internal/server"
moduleService "bsm/full/module/finance/wallet/service"
)
func exposeWallet(srv *server.Server) error {
return moduleService.Expose(moduleService.ExposeOptions{
GRPC: srv.GRPC,
Gateway: srv.Gateway,
Config: config.Spec.Wallet,
})
}