From 0e76d6dfca13b14fd8beda658fd996dec5684407 Mon Sep 17 00:00:00 2001 From: zzw <1464003642@qq.com> Date: Fri, 21 Aug 2026 14:59:08 +0800 Subject: [PATCH] fix: ci dir error --- .gitea/workflows/ci.yml | 51 +- README-NEW.md | 330 ----------- README.md | 162 ++---- bff/Dockerfile | 1 + rpc/ad/ad.pb | Bin 0 -> 14691 bytes rpc/ad/ad.proto | 85 +++ rpc/ad/pb/ad.pb.go | 612 +++++++++++++++++++++ rpc/ad/pb/ad_grpc.pb.go | 243 ++++++++ services/ad/Dockerfile | 39 ++ services/ad/ad.go | 152 +++++ services/ad/adclient/ad.go | 69 +++ services/ad/etc/ad.yaml | 7 + services/ad/internal/config/config.go | 13 + services/ad/internal/dao/ad.go | 38 ++ services/ad/internal/logic/convert.go | 20 + services/ad/internal/logic/createLogic.go | 58 ++ services/ad/internal/logic/editLogic.go | 69 +++ services/ad/internal/logic/itemsLogic.go | 70 +++ services/ad/internal/logic/response.go | 28 + services/ad/internal/logic/statusLogic.go | 67 +++ services/ad/internal/model/ad_model.go | 23 + services/ad/internal/server/adServer.go | 48 ++ services/ad/internal/svc/servicecontext.go | 22 + services/ad/run.toml | 48 ++ services/ad/validator/validator.go | 74 +++ 25 files changed, 1860 insertions(+), 469 deletions(-) delete mode 100644 README-NEW.md create mode 100644 rpc/ad/ad.pb create mode 100644 rpc/ad/ad.proto create mode 100644 rpc/ad/pb/ad.pb.go create mode 100644 rpc/ad/pb/ad_grpc.pb.go create mode 100644 services/ad/Dockerfile create mode 100644 services/ad/ad.go create mode 100644 services/ad/adclient/ad.go create mode 100644 services/ad/etc/ad.yaml create mode 100644 services/ad/internal/config/config.go create mode 100644 services/ad/internal/dao/ad.go create mode 100644 services/ad/internal/logic/convert.go create mode 100644 services/ad/internal/logic/createLogic.go create mode 100644 services/ad/internal/logic/editLogic.go create mode 100644 services/ad/internal/logic/itemsLogic.go create mode 100644 services/ad/internal/logic/response.go create mode 100644 services/ad/internal/logic/statusLogic.go create mode 100644 services/ad/internal/model/ad_model.go create mode 100644 services/ad/internal/server/adServer.go create mode 100644 services/ad/internal/svc/servicecontext.go create mode 100644 services/ad/run.toml create mode 100644 services/ad/validator/validator.go diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 8215ab6..a1315b5 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -6,9 +6,14 @@ on: - develop paths: - "bff/**" - - "product/**" - - "admin/**" - - "user/**" + - "services/product/**" + - "services/admin/**" + - "services/user/**" + - "services/ad/**" + - "rpc/product/**" + - "rpc/admin/**" + - "rpc/user/**" + - "rpc/ad/**" - "pkg/**" - ".gitea/workflows/ci.yml" workflow_dispatch: @@ -21,6 +26,7 @@ jobs: product: ${{ steps.filter.outputs.product }} admin: ${{ steps.filter.outputs.admin }} user: ${{ steps.filter.outputs.user }} + ad: ${{ steps.filter.outputs.ad }} steps: - name: Checkout uses: https://git.ailuowan.com/deploy/checkout@v4 @@ -34,12 +40,14 @@ jobs: product=false admin=false user=false + ad=false if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then bff=true product=true admin=true user=true + ad=true else before="${{ github.event.before }}" if [ -z "$before" ] || [ "$before" = "0000000000000000000000000000000000000000" ]; then @@ -51,17 +59,19 @@ jobs: echo "changed files:" echo "$changed" - echo "$changed" | grep -qE '^(bff|pkg)/|^\.gitea/workflows/ci\.yml$' && bff=true || true - echo "$changed" | grep -qE '^(product|pkg)/|^\.gitea/workflows/ci\.yml$' && product=true || true - echo "$changed" | grep -qE '^(admin|pkg)/|^\.gitea/workflows/ci\.yml$' && admin=true || true - echo "$changed" | grep -qE '^(user|pkg)/|^\.gitea/workflows/ci\.yml$' && user=true || true + echo "$changed" | grep -qE '^(bff|pkg|rpc)/|^\.gitea/workflows/ci\.yml$' && bff=true || true + echo "$changed" | grep -qE '^(services/product|rpc/product|pkg)/|^\.gitea/workflows/ci\.yml$' && product=true || true + echo "$changed" | grep -qE '^(services/admin|rpc/admin|pkg)/|^\.gitea/workflows/ci\.yml$' && admin=true || true + echo "$changed" | grep -qE '^(services/user|rpc/user|pkg)/|^\.gitea/workflows/ci\.yml$' && user=true || true + echo "$changed" | grep -qE '^(services/ad|rpc/ad|pkg)/|^\.gitea/workflows/ci\.yml$' && ad=true || true fi echo "bff=$bff" >> "$GITHUB_OUTPUT" echo "product=$product" >> "$GITHUB_OUTPUT" echo "admin=$admin" >> "$GITHUB_OUTPUT" echo "user=$user" >> "$GITHUB_OUTPUT" - echo "bff=$bff product=$product admin=$admin user=$user" + echo "ad=$ad" >> "$GITHUB_OUTPUT" + echo "bff=$bff product=$product admin=$admin user=$user ad=$ad" docker-bff: needs: changes @@ -162,3 +172,28 @@ jobs: - name: Docker push run: | docker push ${{ vars.REGISTRY }}/$NAME:latest + + docker-ad: + needs: changes + if: needs.changes.outputs.ad == 'true' + runs-on: runner + env: + NAME: lone/ad + steps: + - name: Checkout + uses: https://git.ailuowan.com/deploy/checkout@v4 + + - name: Login Registry + run: | + echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ vars.REGISTRY }} -u "${{ vars.REGISTRY_USERNAME }}" --password-stdin + + - name: Docker build + run: | + docker buildx build --load \ + -f services/ad/Dockerfile \ + -t ${{ vars.REGISTRY }}/$NAME:latest \ + . + + - name: Docker push + run: | + docker push ${{ vars.REGISTRY }}/$NAME:latest diff --git a/README-NEW.md b/README-NEW.md deleted file mode 100644 index 17eadf3..0000000 --- a/README-NEW.md +++ /dev/null @@ -1,330 +0,0 @@ -## 常用命令 - -### 命令 goctl 生成/更新脚手架 - -```bash -cd lone-services/ -rpc/scripts/gen-rpc.sh 服务名 如 product -``` - -**什么时候跑:** - -- 增删改 **字段**(BFF gateway 编解码需要) -- 增删 **RPC** -- 改 **google.api.http** 路由 - - - -## 如何加一个新服务 - -以加 `order` 为例(对标现有 `product`)。 - -### 0. 安装 goctl - -```bash -go install github.com/zeromicro/go-zero/tools/goctl@latest -goctl --version -go install google.golang.org/protobuf/cmd/protoc-gen-go@latest -go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest -``` - -### 1. 创建 RPC 服务(goctl) - -在仓库根目录执行: - -```bash -cd services -goctl rpc new order -cd order -``` - -### 2. 将 proto 移到 `proto/` 目录 - -`goctl rpc new` 默认把 proto 放在服务根目录(如 `order/order.proto`)。统一挪到 `../rpc/order/order.proto`: - -```bash -mkdir ../rpc/proto -mv order.proto proto/ -修改 option go_package="./order"; 为 option go_package="lone-services/rpc/order"; - -``` - -### 3. 删除order目录 - -### 4. Zero 兼容 Nacos 等配置 - -编辑 `order/internal/config/config.go`: - -```go -package config - -type Config struct { - Nacos NacosConf -} - -type NacosConf struct { - Hosts []string - NamespaceId string `json:",optional"` - Group string `json:",optional"` - RegisterIP string `json:",optional"` - ConfigID string `json:",optional"` -} - -``` - -### 5. 配置服务名与监听端口 - -编辑 `order/etc/order.yaml`: - -```yaml -Nacos: - Hosts: - - host.docker.internal:8848 - NamespaceId: test - Group: LONE_SERVICES - RegisterIP: order - ConfigID: develop-order -``` - - -### 6. 服务注册 - -编辑 `order/order.go`: - -增加初始化 - -```go - -func main() { -flag.Parse() -var c config.Config -conf.MustLoad(*configFile, &c) - -// Nacos配置拉取初始化 -nacosParam := utils.NacosConfig{ -Hosts: c.Nacos.Hosts, -NamespaceId: c.Nacos.NamespaceId, -Group: c.Nacos.Group, -ConfigID: c.Nacos.ConfigID, -} -utils.InitConfig(nacosParam) - -// 日志配置 -logConf := logx.LogConf{ -ServiceName: utils.GetConfigString("log.serviceName"), -Mode: utils.GetConfigString("log.mode"), -Encoding: utils.GetConfigString("log.encoding"), -Level: utils.GetConfigString("log.level"), -Path: utils.GetConfigString("log.path"), -KeepDays: utils.GetConfigInt("log.keepDays"), -MaxSize: utils.GetConfigInt("log.maxSize"), -MaxBackups: utils.GetConfigInt("log.maxBackups"), -Compress: utils.GetConfigBool("log.compress"), -} -logx.SetUp(logConf) -logx.AddWriter(logx.NewWriter(os.Stdout)) - -listenOn := utils.GetConfigString("base.listenOn") -mode := utils.GetConfigString("base.mode") -serviceName := utils.GetConfigString("base.name") - -// 初始化服务发现 -if err := discovery.Init(discovery.Config{ -Hosts: c.Nacos.Hosts, -NamespaceId: c.Nacos.NamespaceId, -Group: c.Nacos.Group, -}); err != nil { -logx.Errorf("nacos init: %v", err) -os.Exit(1) -} - -port, err := listenPort(listenOn) -if err != nil { -logx.Errorf("parse ListenOn: %v", err) -os.Exit(1) -} - -// 注册服务 -if err := discovery.Register(discovery.Instance{ -ServiceName: serviceName, -IP: c.Nacos.RegisterIP, -Port: port, -Group: c.Nacos.Group, -}); err != nil { -logx.Errorf("nacos register: %v", err) -os.Exit(1) -} -logx.Infof("服务注册成功: %s:%d", c.Nacos.RegisterIP, port) - -defer func() { -if err := discovery.Deregister(); err != nil { -logx.Errorf("nacos deregister: %v", err) -} -}() - -// 初始化MySQL -db, err := mysql.New(mysql.Config{ -Host: utils.GetConfigString("mysql.host"), -Port: utils.GetConfigInt("mysql.port"), -User: utils.GetConfigString("mysql.user"), -Password: utils.GetConfigString("mysql.password"), -Database: utils.GetConfigString("mysql.database"), -Charset: utils.GetConfigString("mysql.charset"), -Prefix: utils.GetConfigString("mysql.prefix"), -ReadHost: utils.GetConfigString("mysql_read.host"), -ReadPort: utils.GetConfigInt("mysql_read.port"), -ReadUser: utils.GetConfigString("mysql_read.user"), -ReadPassword: utils.GetConfigString("mysql_read.password"), -ReadDatabase: utils.GetConfigString("mysql_read.database"), -}) -if err != nil { -logx.Errorf("mysql init: %v", err) -os.Exit(1) -} - -// 初始化Redis -if err := redis.Init(redis.Config{ -Host: utils.GetConfigString("redis.host"), -Port: utils.GetConfigInt("redis.port"), -Password: utils.GetConfigString("redis.password"), -DB: utils.GetConfigInt("redis.db"), -}); err != nil { -logx.Errorf("redis init: %v", err) -os.Exit(1) -} - -//日志队列初始化 -if err := logRedis.Init(logRedis.LogRedisConfig{ -Host: utils.GetConfigString("log-redis.host"), -Port: utils.GetConfigInt("log-redis.port"), -Password: utils.GetConfigString("log-redis.password"), -DB: utils.GetConfigInt("log-redis.db"), -}); err != nil { -logx.Errorf("log redis init: %v", err) -os.Exit(1) -} - -debug := utils.GetConfigBool("mysql.debug") -modelbase.Init(db, modelbase.Config{Prefix: utils.GetConfigString("mysql.prefix"), Debug: debug}) - -// 初始化服务信息 -rpcConf := zrpc.RpcServerConf{ -ListenOn: listenOn, -} -rpcConf.Mode = mode -ctx := svc.NewServiceContext(c, db) - -s := zrpc.MustNewServer(rpcConf, func(grpcServer *grpc.Server) { -order.RegisterOrderServer(grpcServer, server.NewOrderServer(ctx)) -if mode == service.DevMode || mode == service.TestMode { -reflection.Register(grpcServer) -} -}) -defer s.Stop() - -s.AddUnaryInterceptors(validate.UnaryServerInterceptor(validate.MustNew())) - -logx.Infof("Starting rpc server at %s...", listenOn) -s.Start() -} - -// listenPort 拆分端口 -func listenPort(listenOn string) (uint64, error) { -_, portStr, err := net.SplitHostPort(listenOn) -if err != nil { -return utils.NumberZero, err -} -return strconv.ParseUint(portStr, utils.NumberTen, utils.NumberSixtyFourth) -} - - -``` - -执行 tidy - -```bash -go mod tidy -``` - -编辑 `internal/svc/servicecontext.go`: - -```go -package svc - -import ( - "lone-services/pkg/utils" - "lone-services/services/order/internal/config" - - "gorm.io/gorm" -) - -type ServiceContext struct { - Config config.Config - DB *gorm.DB - Prefix string -} - -func NewServiceContext(c config.Config, db *gorm.DB) *ServiceContext { - return &ServiceContext{ - Config: c, - DB: db, - Prefix: utils.GetConfigString("mysql.prefix"), - } -} - - -``` - -### 7. 接入 Docker Compose - -在 `deploy/docker-compose.dev.override.yml` 增加服务: - -```yaml - order: - image: golang:1.26.5 - volumes: - - ..:/src - - go-mod-cache:/go/pkg/mod - - go-build-cache:/root/.cache/go-build - working_dir: /src/services/order - command: go run . -f etc/order.yaml - ports: - - "10200:10200" - environment: - NACOS_SERVER_ADDR: rnacos:8848 - depends_on: - - rnacos -``` - -### 9. 在 BFF 配 Upstream(HTTP 路由写在 proto) - -编辑 `bff/etc/bff.yaml`,在 **Upstreams** 增加一段(不必写 Mappings,gateway 会从 ProtoSet 里的 http option 注册路由): - -```yaml - - Name: order - Grpc: - Target: order-service # 必须等于 order.yaml 的 Name - Timeout: 5000 - ProtoSets: - - etc/order.pb -``` - -在对应 RPC 上声明 `google.api.http`(需 `import "google/api/annotations.proto";`): - -```protobuf -rpc Ping(Request) returns (Response) { - option (google.api.http) = { - post: "/admin/v3/order/ping" - body: "*" - }; -} -``` - -### 编写业务须知 - -只改业务逻辑(`internal/logic/`)不必跑 goctl。 -改 `proto/order.proto`(新增/改名 RPC、改字段、改 http option 等)后,按文末「三条命令何时用」处理; - -若 HTTP 路径有变,改 proto 里的 `google.api.http` 后重新跑命令 ② 生成 ProtoSet。 - ---- \ No newline at end of file diff --git a/README.md b/README.md index 98e7505..37dd270 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,9 @@ ## 常用命令 -### 命令 ①:goctl 生成/更新脚手架 - ```bash -goctl -I. -I../pkg/third_party rpc protoc proto/order.proto --go_out=. --go-grpc_out=. --zrpc_out=. +rpc/scripts/gen-rpc.sh order ``` -### 命令 ②:生成服务端 pb / grpc - -```bash -goctl rpc protoc proto/order.proto --proto_path=. --proto_path=../pkg/third_party --go_out=. --go-grpc_out=. --zrpc_out=. -``` - -**什么时候跑:** - -- 只改 **校验规则**(必填/长度/范围等)→ **只跑这条** -- 增删改字段、增删 RPC → 也要跑(服务端描述符要更新) - -### 命令 ③:生成 BFF ProtoSet - -```bash -protoc -I. -I../pkg/third_party --descriptor_set_out=../bff/etc/order.pb --include_imports proto/order.proto -``` - -**什么时候跑:** - -- 增删改 **字段**(BFF gateway 编解码需要) -- 增删 **RPC** -- 改 **google.api.http** 路由 - ## 如何加一个新服务 以加 `order` 为例(对标现有 `product`)。 @@ -47,20 +22,36 @@ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest 在仓库根目录执行: ```bash +cd services goctl rpc new order -cd order +cd ../rpc +mkdir order + ``` -### 2. 将 proto 移到 `proto/` 目录 +### 2. 将 proto 移到 `rpc/` 目录 -`goctl rpc new` 默认把 proto 放在服务根目录(如 `order/order.proto`)。统一挪到 `proto/`: +`goctl rpc new` 默认把 proto 放在服务根目录(如 `order/order.proto`)。统一挪到 `rpc/`: ```bash -mkdir proto -mv order.proto proto/ +cd ../services/order +mv order.proto ../../rpc/order/ ``` -### 3. Zero 兼容 Nacos 等配置 +编辑 `rpc/order/order.proto`,把 `go_package` 改成本仓库模块路径: + +```protobuf +option go_package = "lone-services/rpc/order"; +``` + +### 3. 生成pb 文件 + +windows 用 git 的 bash 跑 +```bash +D:\Git\bin\bash.exe rpc/scripts/gen-rpc.sh order +``` + +### 4. Zero 兼容 Nacos 等配置 编辑 `order/internal/config/config.go`: @@ -80,47 +71,22 @@ type NacosConf struct { } ``` -### 4. 配置服务名与监听端口 +### 5. 配置服务名与监听端口 编辑 `order/etc/order.yaml`: ```yaml Nacos: Hosts: - - host.docker.internal:8848 + - rnacos:8848 NamespaceId: test Group: LONE_SERVICES - RegisterIP: admin - ConfigID: develop-admin + RegisterIP: order + ConfigID: order ``` -### 5. 构建 mod - -编辑 `order/go.mod` 增加本地包 pkg.local - -```go -go 1.26 - -replace pkg.local => ../pkg -``` - - - ### 6. 服务注册 -编辑 `order/order.go`: - -增加 import - -```go -"pkg.local/discovery" -"pkg.local/modelbase" -"pkg.local/mysql" -"pkg.local/redis" -"pkg.local/validate" -"pkg.local/utils" -``` - 增加初始化 ```go @@ -251,12 +217,6 @@ func listenPort(listenOn string) (uint64, error) { ``` -执行 tidy - -```bash -go mod tidy -``` - 编辑 `internal/svc/servicecontext.go`: ```go @@ -279,7 +239,7 @@ func NewServiceContext(c config.Config, db *gorm.DB) *ServiceContext { ### 7. 接入 Docker Compose -在 `deploy/docker-compose.dev.override.yml` 增加服务: +在 `deploy/docker-compose.override.yml` 增加服务: ```yaml order: @@ -288,7 +248,7 @@ func NewServiceContext(c config.Config, db *gorm.DB) *ServiceContext { - ..:/src - go-mod-cache:/go/pkg/mod - go-build-cache:/root/.cache/go-build - working_dir: /src/order + working_dir: /src/services/order command: go run . -f etc/order.yaml ports: - "10200:10200" @@ -298,13 +258,7 @@ func NewServiceContext(c config.Config, db *gorm.DB) *ServiceContext { - rnacos ``` -### 8. 生成 ProtoSet 给 BFF - -```bash -protoc --include_imports --proto_path=proto --descriptor_set_out=../bff/etc/order.pb order.proto -``` - -### 9. 在 BFF 配 Upstream(HTTP 路由写在 proto) +### 8. 在 BFF 配 Upstream(HTTP 路由写在 proto) 编辑 `bff/etc/bff.yaml`,在 **Upstreams** 增加一段(不必写 Mappings,gateway 会从 ProtoSet 里的 http option 注册路由): @@ -314,63 +268,9 @@ protoc --include_imports --proto_path=proto --descriptor_set_out=../bff/etc/orde Target: order-service # 必须等于 order.yaml 的 Name Timeout: 5000 ProtoSets: - - etc/order.pb + - ../rpc/order/order.pb ``` -在对应 RPC 上声明 `google.api.http`(需 `import "google/api/annotations.proto";`): - -```protobuf -rpc Ping(Request) returns (Response) { - option (google.api.http) = { - post: "/admin/v3/order/ping" - body: "*" - }; -} -``` - -### 编写业务须知 - -只改业务逻辑(`internal/logic/`)不必跑 goctl。 -改 `proto/order.proto`(新增/改名 RPC、改字段、改 http option 等)后,按文末「三条命令何时用」处理; - -若 HTTP 路径有变,改 proto 里的 `google.api.http` 后重新跑命令 ② 生成 ProtoSet。 - ---- - -### 命令 ①:生成服务端 pb / grpc - -```bash -goctl rpc protoc proto/order.proto --proto_path=. --proto_path=../pkg/third_party --go_out=. --go-grpc_out=. --zrpc_out=. -``` - -**什么时候跑:** - -- 只改 **校验规则**(必填/长度/范围等)→ **只跑这条** -- 增删改字段、增删 RPC → 也要跑(服务端描述符要更新) - -### 命令 ②:生成 BFF ProtoSet - -```bash -protoc -I. -I../pkg/third_party --descriptor_set_out=../bff/etc/order.pb --include_imports proto/order.proto -``` - -**什么时候跑:** - -- 增删改 **字段**(BFF gateway 编解码需要) -- 增删 **RPC** -- 改 **google.api.http** 路由 - -### 命令 ③:goctl 生成/更新脚手架 - -```bash -goctl -I. -I../pkg/third_party rpc protoc proto/order.proto --go_out=. --go-grpc_out=. --zrpc_out=. -``` - -**什么时候跑:** - -- **新增 RPC**:需要生成 `internal/logic`、更新 `server` / `orderclient` 等 -- 注意:可能覆盖已改过的 `order.go` 等,生成后对比合并;也可用手写 logic/server 代替 - ## Proto 常用校验规则(protovalidate) 文档:[https://protovalidate.com/schemas/standard-rules/](https://protovalidate.com/schemas/standard-rules/) diff --git a/bff/Dockerfile b/bff/Dockerfile index ba251d3..121f825 100644 --- a/bff/Dockerfile +++ b/bff/Dockerfile @@ -36,6 +36,7 @@ COPY rpc/user/*.pb \ rpc/order/*.pb \ rpc/product/*.pb \ rpc/admin/*.pb \ + rpc/ad/*.pb \ rpc/lonelog/*.pb \ ./etc/ diff --git a/rpc/ad/ad.pb b/rpc/ad/ad.pb new file mode 100644 index 0000000000000000000000000000000000000000..9a5b3112c81c57b2d8744aac2c32cbbc08b33cb5 GIT binary patch literal 14691 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\n" + + "\x06Create\x12\r.ad.CreateReq\x1a\f.ad.Response\"\x17\x82\xd3\xe4\x93\x02\x11:\x01*\"\f/admin/v3/ad\x12:\n" + + "\x04Edit\x12\v.ad.EditReq\x1a\f.ad.Response\"\x17\x82\xd3\xe4\x93\x02\x11:\x01*\x1a\f/admin/v3/ad\x12<\n" + + "\x05Items\x12\f.ad.ItemsReq\x1a\f.ad.Response\"\x17\x82\xd3\xe4\x93\x02\x11:\x01*\x12\f/admin/v3/ad\x12E\n" + + "\x06Status\x12\r.ad.StatusReq\x1a\f.ad.Response\"\x1e\x82\xd3\xe4\x93\x02\x18:\x01*\x1a\x13/admin/v3/ad/statusB\x16Z\x14lone-services/rpc/adb\x06proto3" + +var ( + file_ad_ad_proto_rawDescOnce sync.Once + file_ad_ad_proto_rawDescData []byte +) + +func file_ad_ad_proto_rawDescGZIP() []byte { + file_ad_ad_proto_rawDescOnce.Do(func() { + file_ad_ad_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ad_ad_proto_rawDesc), len(file_ad_ad_proto_rawDesc))) + }) + return file_ad_ad_proto_rawDescData +} + +var file_ad_ad_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_ad_ad_proto_goTypes = []any{ + (*Response)(nil), // 0: ad.Response + (*CreateReq)(nil), // 1: ad.CreateReq + (*EditReq)(nil), // 2: ad.EditReq + (*ItemsReq)(nil), // 3: ad.ItemsReq + (*StatusReq)(nil), // 4: ad.StatusReq + (*ItemsData)(nil), // 5: ad.ItemsData + (*Item)(nil), // 6: ad.Item +} +var file_ad_ad_proto_depIdxs = []int32{ + 6, // 0: ad.ItemsData.items:type_name -> ad.Item + 1, // 1: ad.Ad.Create:input_type -> ad.CreateReq + 2, // 2: ad.Ad.Edit:input_type -> ad.EditReq + 3, // 3: ad.Ad.Items:input_type -> ad.ItemsReq + 4, // 4: ad.Ad.Status:input_type -> ad.StatusReq + 0, // 5: ad.Ad.Create:output_type -> ad.Response + 0, // 6: ad.Ad.Edit:output_type -> ad.Response + 0, // 7: ad.Ad.Items:output_type -> ad.Response + 0, // 8: ad.Ad.Status:output_type -> ad.Response + 5, // [5:9] is the sub-list for method output_type + 1, // [1:5] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_ad_ad_proto_init() } +func file_ad_ad_proto_init() { + if File_ad_ad_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_ad_ad_proto_rawDesc), len(file_ad_ad_proto_rawDesc)), + NumEnums: 0, + NumMessages: 7, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_ad_ad_proto_goTypes, + DependencyIndexes: file_ad_ad_proto_depIdxs, + MessageInfos: file_ad_ad_proto_msgTypes, + }.Build() + File_ad_ad_proto = out.File + file_ad_ad_proto_goTypes = nil + file_ad_ad_proto_depIdxs = nil +} diff --git a/rpc/ad/pb/ad_grpc.pb.go b/rpc/ad/pb/ad_grpc.pb.go new file mode 100644 index 0000000..afa6510 --- /dev/null +++ b/rpc/ad/pb/ad_grpc.pb.go @@ -0,0 +1,243 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.2 +// - protoc v3.19.4 +// source: ad/ad.proto + +package ad + +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 ( + Ad_Create_FullMethodName = "/ad.Ad/Create" + Ad_Edit_FullMethodName = "/ad.Ad/Edit" + Ad_Items_FullMethodName = "/ad.Ad/Items" + Ad_Status_FullMethodName = "/ad.Ad/Status" +) + +// AdClient is the client API for Ad 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 AdClient interface { + // 添加广告 + Create(ctx context.Context, in *CreateReq, opts ...grpc.CallOption) (*Response, error) + // 编辑广告 + Edit(ctx context.Context, in *EditReq, opts ...grpc.CallOption) (*Response, error) + // 广告列表 + Items(ctx context.Context, in *ItemsReq, opts ...grpc.CallOption) (*Response, error) + // 广告状态 + Status(ctx context.Context, in *StatusReq, opts ...grpc.CallOption) (*Response, error) +} + +type adClient struct { + cc grpc.ClientConnInterface +} + +func NewAdClient(cc grpc.ClientConnInterface) AdClient { + return &adClient{cc} +} + +func (c *adClient) Create(ctx context.Context, in *CreateReq, opts ...grpc.CallOption) (*Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Response) + err := c.cc.Invoke(ctx, Ad_Create_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adClient) Edit(ctx context.Context, in *EditReq, opts ...grpc.CallOption) (*Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Response) + err := c.cc.Invoke(ctx, Ad_Edit_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adClient) Items(ctx context.Context, in *ItemsReq, opts ...grpc.CallOption) (*Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Response) + err := c.cc.Invoke(ctx, Ad_Items_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adClient) Status(ctx context.Context, in *StatusReq, opts ...grpc.CallOption) (*Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Response) + err := c.cc.Invoke(ctx, Ad_Status_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AdServer is the server API for Ad service. +// All implementations must embed UnimplementedAdServer +// for forward compatibility. +type AdServer interface { + // 添加广告 + Create(context.Context, *CreateReq) (*Response, error) + // 编辑广告 + Edit(context.Context, *EditReq) (*Response, error) + // 广告列表 + Items(context.Context, *ItemsReq) (*Response, error) + // 广告状态 + Status(context.Context, *StatusReq) (*Response, error) + mustEmbedUnimplementedAdServer() +} + +// UnimplementedAdServer 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 UnimplementedAdServer struct{} + +func (UnimplementedAdServer) Create(context.Context, *CreateReq) (*Response, error) { + return nil, status.Error(codes.Unimplemented, "method Create not implemented") +} +func (UnimplementedAdServer) Edit(context.Context, *EditReq) (*Response, error) { + return nil, status.Error(codes.Unimplemented, "method Edit not implemented") +} +func (UnimplementedAdServer) Items(context.Context, *ItemsReq) (*Response, error) { + return nil, status.Error(codes.Unimplemented, "method Items not implemented") +} +func (UnimplementedAdServer) Status(context.Context, *StatusReq) (*Response, error) { + return nil, status.Error(codes.Unimplemented, "method Status not implemented") +} +func (UnimplementedAdServer) mustEmbedUnimplementedAdServer() {} +func (UnimplementedAdServer) testEmbeddedByValue() {} + +// UnsafeAdServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AdServer will +// result in compilation errors. +type UnsafeAdServer interface { + mustEmbedUnimplementedAdServer() +} + +func RegisterAdServer(s grpc.ServiceRegistrar, srv AdServer) { + // If the following call panics, it indicates UnimplementedAdServer 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(&Ad_ServiceDesc, srv) +} + +func _Ad_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdServer).Create(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Ad_Create_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdServer).Create(ctx, req.(*CreateReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Ad_Edit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EditReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdServer).Edit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Ad_Edit_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdServer).Edit(ctx, req.(*EditReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Ad_Items_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ItemsReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdServer).Items(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Ad_Items_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdServer).Items(ctx, req.(*ItemsReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Ad_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StatusReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdServer).Status(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Ad_Status_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdServer).Status(ctx, req.(*StatusReq)) + } + return interceptor(ctx, in, info, handler) +} + +// Ad_ServiceDesc is the grpc.ServiceDesc for Ad service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Ad_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "ad.Ad", + HandlerType: (*AdServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Create", + Handler: _Ad_Create_Handler, + }, + { + MethodName: "Edit", + Handler: _Ad_Edit_Handler, + }, + { + MethodName: "Items", + Handler: _Ad_Items_Handler, + }, + { + MethodName: "Status", + Handler: _Ad_Status_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ad/ad.proto", +} diff --git a/services/ad/Dockerfile b/services/ad/Dockerfile new file mode 100644 index 0000000..3b45dd3 --- /dev/null +++ b/services/ad/Dockerfile @@ -0,0 +1,39 @@ +FROM golang:1.26.5-alpine AS builder + +WORKDIR /src + +ENV GOPROXY=https://goproxy.cn,direct \ + CGO_ENABLED=0 \ + GOOS=linux \ + GOARCH=amd64 + +COPY pkg ./pkg +COPY go.mod go.sum ./ + +RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download + +COPY rpc/ad/ ./rpc/ad/ +COPY services/ad/ ./services/ad/ + +WORKDIR /src/services/ad + +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go build -ldflags="-s -w" -o /out/main ./ + +FROM alpine:3.22 + +WORKDIR /app + +ENV TZ=Asia/Shanghai + +RUN apk add --no-cache tzdata \ + && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone + +COPY --from=builder /out/main . + +EXPOSE 10400 + +CMD ["./main", "-f", "etc/ad.yaml"] diff --git a/services/ad/ad.go b/services/ad/ad.go new file mode 100644 index 0000000..b296674 --- /dev/null +++ b/services/ad/ad.go @@ -0,0 +1,152 @@ +package main + +import ( + "flag" + "net" + "os" + "strconv" + + "lone-services/pkg/discovery" + "lone-services/pkg/modelbase" + "lone-services/pkg/mysql" + "lone-services/pkg/redis" + "lone-services/pkg/utils" + "lone-services/pkg/validate" + ad "lone-services/rpc/ad/pb" + "lone-services/services/ad/internal/config" + "lone-services/services/ad/internal/server" + "lone-services/services/ad/internal/svc" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/service" + "github.com/zeromicro/go-zero/zrpc" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var configFile = flag.String("f", "etc/ad.yaml", "the config file") + +func main() { + flag.Parse() + var c config.Config + conf.MustLoad(*configFile, &c) + + nacosParam := utils.NacosConfig{ + Hosts: c.Nacos.Hosts, + NamespaceId: c.Nacos.NamespaceId, + Group: c.Nacos.Group, + ConfigID: c.Nacos.ConfigID, + } + utils.InitConfig(nacosParam) + + logConf := logx.LogConf{ + ServiceName: utils.GetConfigString("log.serviceName"), + Mode: utils.GetConfigString("log.mode"), + Encoding: utils.GetConfigString("log.encoding"), + Level: utils.GetConfigString("log.level"), + Path: utils.GetConfigString("log.path"), + KeepDays: utils.GetConfigInt("log.keepDays"), + MaxSize: utils.GetConfigInt("log.maxSize"), + MaxBackups: utils.GetConfigInt("log.maxBackups"), + Compress: utils.GetConfigBool("log.compress"), + } + logx.SetUp(logConf) + logx.AddWriter(logx.NewWriter(os.Stdout)) + + listenOn := utils.GetConfigString("base.listenOn") + mode := utils.GetConfigString("base.mode") + serviceName := utils.GetConfigString("base.name") + + if err := discovery.Init(discovery.Config{ + Hosts: c.Nacos.Hosts, + NamespaceId: c.Nacos.NamespaceId, + Group: c.Nacos.Group, + }); err != nil { + logx.Errorf("nacos init: %v", err) + os.Exit(1) + } + + port, err := listenPort(listenOn) + if err != nil { + logx.Errorf("parse ListenOn: %v", err) + os.Exit(1) + } + + if err := discovery.Register(discovery.Instance{ + ServiceName: serviceName, + IP: c.Nacos.RegisterIP, + Port: port, + Group: c.Nacos.Group, + }); err != nil { + logx.Errorf("nacos register: %v", err) + os.Exit(1) + } + logx.Infof("服务注册成功: %s:%d", c.Nacos.RegisterIP, port) + + defer func() { + if err := discovery.Deregister(); err != nil { + logx.Errorf("nacos deregister: %v", err) + } + }() + + db, err := mysql.New(mysql.Config{ + Host: utils.GetConfigString("mysql.host"), + Port: utils.GetConfigInt("mysql.port"), + User: utils.GetConfigString("mysql.user"), + Password: utils.GetConfigString("mysql.password"), + Database: utils.GetConfigString("mysql.database"), + Charset: utils.GetConfigString("mysql.charset"), + Prefix: utils.GetConfigString("mysql.prefix"), + ReadHost: utils.GetConfigString("mysql_read.host"), + ReadPort: utils.GetConfigInt("mysql_read.port"), + ReadUser: utils.GetConfigString("mysql_read.user"), + ReadPassword: utils.GetConfigString("mysql_read.password"), + ReadDatabase: utils.GetConfigString("mysql_read.database"), + }) + if err != nil { + logx.Errorf("mysql init: %v", err) + os.Exit(1) + } + + if err := redis.Init(redis.Config{ + Host: utils.GetConfigString("redis.host"), + Port: utils.GetConfigInt("redis.port"), + Password: utils.GetConfigString("redis.password"), + DB: utils.GetConfigInt("redis.db"), + }); err != nil { + logx.Errorf("redis init: %v", err) + os.Exit(1) + } + + debug := utils.GetConfigBool("mysql.debug") + modelbase.Init(db, modelbase.Config{Prefix: utils.GetConfigString("mysql.prefix"), Debug: debug}) + + rpcConf := zrpc.RpcServerConf{ + ListenOn: listenOn, + } + rpcConf.Mode = mode + + ctx := svc.NewServiceContext(c, db) + + s := zrpc.MustNewServer(rpcConf, func(grpcServer *grpc.Server) { + ad.RegisterAdServer(grpcServer, server.NewAdServer(ctx)) + if mode == service.DevMode || mode == service.TestMode { + reflection.Register(grpcServer) + } + }) + defer s.Stop() + + s.AddUnaryInterceptors(validate.UnaryServerInterceptor(validate.MustNew())) + + logx.Infof("Starting rpc server at %s...", listenOn) + s.Start() +} + +func listenPort(listenOn string) (uint64, error) { + _, portStr, err := net.SplitHostPort(listenOn) + if err != nil { + return 0, err + } + return strconv.ParseUint(portStr, 10, 64) +} diff --git a/services/ad/adclient/ad.go b/services/ad/adclient/ad.go new file mode 100644 index 0000000..e74b5a4 --- /dev/null +++ b/services/ad/adclient/ad.go @@ -0,0 +1,69 @@ +// Code generated by goctl. DO NOT EDIT. +// goctl 1.10.1 +// Source: ad.proto + +package adClient + +import ( + "context" + + "lone-services/rpc/ad/pb" + + "github.com/zeromicro/go-zero/zrpc" + "google.golang.org/grpc" +) + +type ( + CreateReq = ad.CreateReq + EditReq = ad.EditReq + Item = ad.Item + ItemsData = ad.ItemsData + ItemsReq = ad.ItemsReq + Response = ad.Response + StatusReq = ad.StatusReq + + Ad interface { + // 添加广告 + Create(ctx context.Context, in *CreateReq, opts ...grpc.CallOption) (*Response, error) + // 编辑广告 + Edit(ctx context.Context, in *EditReq, opts ...grpc.CallOption) (*Response, error) + // 广告列表 + Items(ctx context.Context, in *ItemsReq, opts ...grpc.CallOption) (*Response, error) + // 广告状态 + Status(ctx context.Context, in *StatusReq, opts ...grpc.CallOption) (*Response, error) + } + + defaultAd struct { + cli zrpc.Client + } +) + +func NewAd(cli zrpc.Client) Ad { + return &defaultAd{ + cli: cli, + } +} + +// 添加广告 +func (m *defaultAd) Create(ctx context.Context, in *CreateReq, opts ...grpc.CallOption) (*Response, error) { + client := ad.NewAdClient(m.cli.Conn()) + return client.Create(ctx, in, opts...) +} + +// 编辑广告 +func (m *defaultAd) Edit(ctx context.Context, in *EditReq, opts ...grpc.CallOption) (*Response, error) { + client := ad.NewAdClient(m.cli.Conn()) + return client.Edit(ctx, in, opts...) +} + +// 广告列表 +func (m *defaultAd) Items(ctx context.Context, in *ItemsReq, opts ...grpc.CallOption) (*Response, error) { + client := ad.NewAdClient(m.cli.Conn()) + return client.Items(ctx, in, opts...) +} + +// 广告状态 +func (m *defaultAd) Status(ctx context.Context, in *StatusReq, opts ...grpc.CallOption) (*Response, error) { + client := ad.NewAdClient(m.cli.Conn()) + return client.Status(ctx, in, opts...) +} diff --git a/services/ad/etc/ad.yaml b/services/ad/etc/ad.yaml new file mode 100644 index 0000000..72297d9 --- /dev/null +++ b/services/ad/etc/ad.yaml @@ -0,0 +1,7 @@ +Nacos: + Hosts: + - rnacos:8848 + NamespaceId: test + Group: LONE_SERVICES + RegisterIP: ad + ConfigID: ad \ No newline at end of file diff --git a/services/ad/internal/config/config.go b/services/ad/internal/config/config.go new file mode 100644 index 0000000..a1979dd --- /dev/null +++ b/services/ad/internal/config/config.go @@ -0,0 +1,13 @@ +package config + +type Config struct { + Nacos NacosConf +} + +type NacosConf struct { + Hosts []string + NamespaceId string `json:",optional"` + Group string `json:",optional"` + RegisterIP string `json:",optional"` + ConfigID string `json:",optional"` +} diff --git a/services/ad/internal/dao/ad.go b/services/ad/internal/dao/ad.go new file mode 100644 index 0000000..4a8e2a4 --- /dev/null +++ b/services/ad/internal/dao/ad.go @@ -0,0 +1,38 @@ +package dao + +const ( + AdStatusNormal uint8 = 1 // 正常 + AdStatusDisabled uint8 = 2 // 禁用 +) + +// type: 1、商城首页banner 2、商城首页视频 3、商城首页公告 4、商城首页教程 + +type AdCreate struct { + Id int `gorm:"column:id;primaryKey;autoIncrement"` + Type uint8 `gorm:"column:type"` + Image string `gorm:"column:image"` + Content string `gorm:"column:content"` + Url string `gorm:"column:url"` + AdminId int `gorm:"column:admin_id"` + AdminName string `gorm:"column:admin_name"` +} + +type AdInfo struct { + Id int `gorm:"column:id"` + Type uint8 `gorm:"column:type"` + Image string `gorm:"column:image"` + Content string `gorm:"column:content"` + Reason string `gorm:"column:reason"` + Status uint8 `gorm:"column:status"` + Url string `gorm:"column:url"` + UpdateTime string `gorm:"column:update_time"` + CreateTime string `gorm:"column:create_time"` +} + +type AdStatus struct { + Id int `gorm:"column:id;primaryKey"` + Reason string `gorm:"column:reason"` + Status uint8 `gorm:"column:status"` + AdminId int `gorm:"column:admin_id"` + AdminName string `gorm:"column:admin_name"` +} diff --git a/services/ad/internal/logic/convert.go b/services/ad/internal/logic/convert.go new file mode 100644 index 0000000..1b7d3de --- /dev/null +++ b/services/ad/internal/logic/convert.go @@ -0,0 +1,20 @@ +package logic + +import ( + ad "lone-services/rpc/ad/pb" + "lone-services/services/ad/internal/dao" +) + +func toAdItem(row dao.AdInfo) *ad.Item { + return &ad.Item{ + Id: int64(row.Id), + Type: uint32(row.Type), + Image: row.Image, + Content: row.Content, + Reason: row.Reason, + Status: uint32(row.Status), + Url: row.Url, + UpdateTime: row.UpdateTime, + CreateTime: row.CreateTime, + } +} diff --git a/services/ad/internal/logic/createLogic.go b/services/ad/internal/logic/createLogic.go new file mode 100644 index 0000000..8be6ae0 --- /dev/null +++ b/services/ad/internal/logic/createLogic.go @@ -0,0 +1,58 @@ +package logic + +import ( + "context" + "strconv" + + "lone-services/pkg/log" + "lone-services/pkg/utils" + "lone-services/pkg/validate" + ad "lone-services/rpc/ad/pb" + "lone-services/services/ad/internal/dao" + "lone-services/services/ad/internal/model" + "lone-services/services/ad/internal/svc" + "lone-services/services/ad/validator" + + "github.com/zeromicro/go-zero/core/logx" +) + +type CreateLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateLogic { + return &CreateLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +func (l *CreateLogic) Create(in *ad.CreateReq) (*ad.Response, error) { + var req validator.AdCreateValidator + if msg := validate.ValidateFromProto(in, &req); msg != utils.StringEmpty { + return outResponse(utils.ErrorParams, msg), nil + } + + adminInfo := utils.GetUserFromCtx(l.ctx) + if adminInfo.ID < utils.NumberOne { + return failResponse(utils.ErrorNoLoginInfo), nil + } + + data := dao.AdCreate{ + Type: uint8(req.Type), + Image: req.Image, + Content: req.Content, + Url: req.Url, + AdminId: int(adminInfo.ID), + AdminName: adminInfo.Name, + } + if err := (model.AdModel{}.Init().Create(&data)); err != nil { + log.Errorf("create ad: %v", err) + return failResponse(utils.Fail), nil + } + + return okResponse(strconv.Itoa(data.Id)), nil +} diff --git a/services/ad/internal/logic/editLogic.go b/services/ad/internal/logic/editLogic.go new file mode 100644 index 0000000..491ed68 --- /dev/null +++ b/services/ad/internal/logic/editLogic.go @@ -0,0 +1,69 @@ +package logic + +import ( + "context" + "strconv" + + "lone-services/pkg/log" + "lone-services/pkg/modelbase" + "lone-services/pkg/utils" + "lone-services/pkg/validate" + ad "lone-services/rpc/ad/pb" + "lone-services/services/ad/internal/dao" + "lone-services/services/ad/internal/model" + "lone-services/services/ad/internal/svc" + "lone-services/services/ad/validator" + + "github.com/zeromicro/go-zero/core/logx" +) + +type EditLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewEditLogic(ctx context.Context, svcCtx *svc.ServiceContext) *EditLogic { + return &EditLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +func (l *EditLogic) Edit(in *ad.EditReq) (*ad.Response, error) { + var req validator.AdEditValidator + if msg := validate.ValidateFromProto(in, &req); msg != utils.StringEmpty { + return outResponse(utils.ErrorParams, msg), nil + } + + adminInfo := utils.GetUserFromCtx(l.ctx) + if adminInfo.ID < utils.NumberOne { + return failResponse(utils.ErrorNoLoginInfo), nil + } + + w := modelbase.Params{Eq: map[string]string{"id": strconv.FormatInt(req.Id, 10)}} + var info dao.AdCreate + m := model.AdModel{}.Init() + if err := m.GetOne(w, &info); err != nil { + log.Errorf("edit ad get: %v", err) + return failResponse(utils.Fail), nil + } + if info.Id < utils.NumberOne { + return failResponse(utils.ErrorNotFund), nil + } + + info.Type = uint8(req.Type) + info.Image = req.Image + info.Content = req.Content + info.Url = req.Url + info.AdminId = int(adminInfo.ID) + info.AdminName = adminInfo.Name + + if _, err := m.Edit(w, &info); err != nil { + log.Errorf("edit ad: %v", err) + return failResponse(utils.Fail), nil + } + + return okResponse(utils.StringEmpty), nil +} diff --git a/services/ad/internal/logic/itemsLogic.go b/services/ad/internal/logic/itemsLogic.go new file mode 100644 index 0000000..373f8a8 --- /dev/null +++ b/services/ad/internal/logic/itemsLogic.go @@ -0,0 +1,70 @@ +package logic + +import ( + "context" + + "lone-services/pkg/log" + "lone-services/pkg/modelbase" + "lone-services/pkg/utils" + "lone-services/pkg/validate" + ad "lone-services/rpc/ad/pb" + "lone-services/services/ad/internal/dao" + "lone-services/services/ad/internal/model" + "lone-services/services/ad/internal/svc" + "lone-services/services/ad/validator" + + "github.com/zeromicro/go-zero/core/logx" +) + +type ItemsLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewItemsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ItemsLogic { + return &ItemsLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +func (l *ItemsLogic) Items(in *ad.ItemsReq) (*ad.Response, error) { + var req validator.AdItemsValidator + if msg := validate.ValidateFromProto(in, &req); msg != utils.StringEmpty { + return outResponse(utils.ErrorParams, msg), nil + } + + page := int(req.Page) + size := int(req.PageSize) + if page < modelbase.DefaultPage { + page = modelbase.DefaultPage + } + if size < modelbase.DefaultPage { + size = modelbase.DefaultSize + } + + w := modelbase.Params{ + Page: page, + Size: size, + Order: "id desc", + } + + var info []dao.AdInfo + result, err := model.AdModel{}.Init().Page(w, &info) + if err != nil { + log.Errorf("ad items: %v", err) + return failResponse(utils.Fail), nil + } + + items := make([]*ad.Item, 0, len(info)) + for _, row := range info { + items = append(items, toAdItem(row)) + } + + return okResponse(utils.StructToJson(&ad.ItemsData{ + Count: result.Count, + Items: items, + })), nil +} diff --git a/services/ad/internal/logic/response.go b/services/ad/internal/logic/response.go new file mode 100644 index 0000000..9059ef5 --- /dev/null +++ b/services/ad/internal/logic/response.go @@ -0,0 +1,28 @@ +package logic + +import ( + "lone-services/pkg/utils" + ad "lone-services/rpc/ad/pb" +) + +func okResponse(data string) *ad.Response { + return &ad.Response{ + Code: utils.Ok.Code, + Msg: utils.Ok.Msg, + Data: data, + } +} + +func failResponse(status utils.Status) *ad.Response { + return &ad.Response{ + Code: status.Code, + Msg: status.Msg, + } +} + +func outResponse(status utils.Status, msg string) *ad.Response { + return &ad.Response{ + Code: status.Code, + Msg: msg, + } +} diff --git a/services/ad/internal/logic/statusLogic.go b/services/ad/internal/logic/statusLogic.go new file mode 100644 index 0000000..e794144 --- /dev/null +++ b/services/ad/internal/logic/statusLogic.go @@ -0,0 +1,67 @@ +package logic + +import ( + "context" + "strconv" + + "lone-services/pkg/log" + "lone-services/pkg/modelbase" + "lone-services/pkg/utils" + "lone-services/pkg/validate" + ad "lone-services/rpc/ad/pb" + "lone-services/services/ad/internal/dao" + "lone-services/services/ad/internal/model" + "lone-services/services/ad/internal/svc" + "lone-services/services/ad/validator" + + "github.com/zeromicro/go-zero/core/logx" +) + +type StatusLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StatusLogic { + return &StatusLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +func (l *StatusLogic) Status(in *ad.StatusReq) (*ad.Response, error) { + var req validator.AdStatusValidator + if msg := validate.ValidateFromProto(in, &req); msg != utils.StringEmpty { + return outResponse(utils.ErrorParams, msg), nil + } + + w := modelbase.Params{Eq: map[string]string{"id": strconv.FormatInt(req.Id, 10)}} + var info dao.AdStatus + m := model.AdModel{}.Init() + if err := m.GetOne(w, &info); err != nil { + log.Errorf("ad status get: %v", err) + return failResponse(utils.Fail), nil + } + if info.Id < utils.NumberOne { + return failResponse(utils.ErrorNotFund), nil + } + + adminInfo := utils.GetUserFromCtx(l.ctx) + if adminInfo.ID < utils.NumberOne { + return failResponse(utils.ErrorNoLoginInfo), nil + } + + info.Reason = req.Reason + info.Status = uint8(req.Status) + info.AdminId = int(adminInfo.ID) + info.AdminName = adminInfo.Name + + if _, err := m.Edit(w, &info); err != nil { + log.Errorf("ad status edit: %v", err) + return failResponse(utils.Fail), nil + } + + return okResponse(utils.StringEmpty), nil +} diff --git a/services/ad/internal/model/ad_model.go b/services/ad/internal/model/ad_model.go new file mode 100644 index 0000000..f439af1 --- /dev/null +++ b/services/ad/internal/model/ad_model.go @@ -0,0 +1,23 @@ +package model + +import ( + "lone-services/pkg/modelbase" + "lone-services/services/ad/internal/dao" +) + +type AdModel struct { + modelbase.Base +} + +func (m AdModel) TableName() string { + return modelbase.Prefix() + "ad" +} + +func (m AdModel) Init() AdModel { + m.Table = m.TableName() + return m +} + +func (m AdModel) Create(data *dao.AdCreate) error { + return m.Base.Create(data) +} diff --git a/services/ad/internal/server/adServer.go b/services/ad/internal/server/adServer.go new file mode 100644 index 0000000..c1326ac --- /dev/null +++ b/services/ad/internal/server/adServer.go @@ -0,0 +1,48 @@ +// Code generated by goctl. DO NOT EDIT. +// goctl 1.10.1 +// Source: ad.proto + +package server + +import ( + "context" + + "lone-services/rpc/ad/pb" + "lone-services/services/ad/internal/logic" + "lone-services/services/ad/internal/svc" +) + +type AdServer struct { + svcCtx *svc.ServiceContext + ad.UnimplementedAdServer +} + +func NewAdServer(svcCtx *svc.ServiceContext) *AdServer { + return &AdServer{ + svcCtx: svcCtx, + } +} + +// 添加广告 +func (s *AdServer) Create(ctx context.Context, in *ad.CreateReq) (*ad.Response, error) { + l := logic.NewCreateLogic(ctx, s.svcCtx) + return l.Create(in) +} + +// 编辑广告 +func (s *AdServer) Edit(ctx context.Context, in *ad.EditReq) (*ad.Response, error) { + l := logic.NewEditLogic(ctx, s.svcCtx) + return l.Edit(in) +} + +// 广告列表 +func (s *AdServer) Items(ctx context.Context, in *ad.ItemsReq) (*ad.Response, error) { + l := logic.NewItemsLogic(ctx, s.svcCtx) + return l.Items(in) +} + +// 广告状态 +func (s *AdServer) Status(ctx context.Context, in *ad.StatusReq) (*ad.Response, error) { + l := logic.NewStatusLogic(ctx, s.svcCtx) + return l.Status(in) +} diff --git a/services/ad/internal/svc/servicecontext.go b/services/ad/internal/svc/servicecontext.go new file mode 100644 index 0000000..a31f07b --- /dev/null +++ b/services/ad/internal/svc/servicecontext.go @@ -0,0 +1,22 @@ +package svc + +import ( + "lone-services/pkg/utils" + "lone-services/services/ad/internal/config" + + "gorm.io/gorm" +) + +type ServiceContext struct { + Config config.Config + DB *gorm.DB + Prefix string +} + +func NewServiceContext(c config.Config, db *gorm.DB) *ServiceContext { + return &ServiceContext{ + Config: c, + DB: db, + Prefix: utils.GetConfigString("mysql.prefix"), + } +} diff --git a/services/ad/run.toml b/services/ad/run.toml new file mode 100644 index 0000000..91b7b3a --- /dev/null +++ b/services/ad/run.toml @@ -0,0 +1,48 @@ +[base] + login_out_time=43200 + login_refresh_out_time=83200 + name = "ad-service" + listenOn = "0.0.0.0:10400" + mode = "dev" + super="1,2" +[log] + path = "logs" + serviceName = "ad-service" + mode = "file" + encoding = "plain" + level = "info" + keepDays = 7 + maxSize = 50 + maxBackups = 5 + compress = false +# 测试的地址 +[mysql] + host = '39.106.171.204' + port = 33066 + user = 'root' + password = 'MOLXRZNOU4Y4' + database = 'dms-ad' + charset = 'utf8mb4' + prefix = '' + debug = true +[mysql_read] + host = '39.106.171.204' + port = 33066 + user = 'root' + password = 'MOLXRZNOU4Y4' + database = 'dms-ad' + charset = 'utf8mb4' + prefix = '' + +[redis] + host = '39.106.171.204' + password = 'lLMLcuPpzSj' + port = 6379 + db = 0 +[log-redis] + host = '39.106.171.204' + password = 'lLMLcuPpzSj' + port = 6379 + db = 3 +[encrypt] + data_key = "u2t9T3luZtoRfhBstkFN6TiIMW38BA8a" #内容数据, \ No newline at end of file diff --git a/services/ad/validator/validator.go b/services/ad/validator/validator.go new file mode 100644 index 0000000..33b8763 --- /dev/null +++ b/services/ad/validator/validator.go @@ -0,0 +1,74 @@ +package validator + +import "lone-services/pkg/validate" + +type AdCreateValidator struct { + Type uint32 `validate:"required,oneof=1 2 3 4"` + Image string `validate:"required_without=Content,max=255"` + Content string `validate:"required_without=Image,max=255"` + Url string `validate:"max=255"` +} + +func (p AdCreateValidator) GetMessage() validate.ValidatorMessages { + return validate.ValidatorMessages{ + "Type.required": "请选择类型", + "Type.oneof": "类型不对", + "Image.required_without": "图片与文本内容必须填写一个", + "Content.required_without": "图片与文本内容必须填写一个", + "Image.max": "图片长度不能超过255", + "Content.max": "内容长度不能超过255", + "Url.max": "链接长度不能超过255", + } +} + +type AdEditValidator struct { + Id int64 `validate:"required,gt=0"` + Type uint32 `validate:"required,oneof=1 2 3 4"` + Image string `validate:"required_without=Content,max=255"` + Content string `validate:"required_without=Image,max=255"` + Url string `validate:"max=255"` +} + +func (p AdEditValidator) GetMessage() validate.ValidatorMessages { + return validate.ValidatorMessages{ + "Id.required": "ID不能为空", + "Id.gt": "ID必须大于0", + "Type.required": "请选择类型", + "Type.oneof": "类型不对", + "Image.required_without": "图片与文本内容必须填写一个", + "Content.required_without": "图片与文本内容必须填写一个", + "Image.max": "图片长度不能超过255", + "Content.max": "内容长度不能超过255", + "Url.max": "链接长度不能超过255", + } +} + +type AdItemsValidator struct { + Page uint32 `validate:"omitempty,min=1"` + PageSize uint32 `validate:"omitempty,min=1,max=100"` +} + +func (p AdItemsValidator) GetMessage() validate.ValidatorMessages { + return validate.ValidatorMessages{ + "Page.min": "页码最小为1", + "PageSize.min": "每页数量最小为1", + "PageSize.max": "每页数量最大为100", + } +} + +type AdStatusValidator struct { + Id int64 `validate:"required,gt=0"` + Status uint32 `validate:"required,oneof=1 2"` + Reason string `validate:"required_if=Status 2,max=255"` +} + +func (p AdStatusValidator) GetMessage() validate.ValidatorMessages { + return validate.ValidatorMessages{ + "Id.required": "ID不能为空", + "Id.gt": "ID必须大于0", + "Status.required": "状态不能为空", + "Status.oneof": "状态不对", + "Reason.required_if": "禁用状态,理由不能为空", + "Reason.max": "理由长度不能超过255", + } +}