Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,12 @@ bin/
.qwen/skills/

# Local certs (generated)
config/certs/*.pem
config/certs/*.zip
configs/certs/*.pem
configs/certs/*.zip

# Local config (may contain secrets)
config/config.yaml
config/local/
config/bvt/
config/test/
config/prod/
config/integration/
# NOTE: environment configs are now tracked; only config.yaml (potential secrets) is ignored
configs/config.yaml

# Dependencies
vendor/
Expand Down
62 changes: 54 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ kratos-develop-template/
│ ├── middleware/ # 中间件
│ └── conf/ # 配置定义 (protobuf 生成)
│ └── conf.proto # 配置结构体定义
├── config/
│ └── config.yaml # 运行时配置
├── configs/ # 多环境配置
│ ├── config.yaml # 运行时配置(不提交,可能含密钥)
│ ├── config.example.yaml # 示例配置
│ ├── local/ # 本地开发
│ ├── prod/ # 生产环境
│ └── integration/ # 集成测试
├── third_party/ # 第三方 proto 依赖
├── go.mod
├── go.sum
Expand Down Expand Up @@ -94,12 +98,32 @@ go build -o bin/server ./cmd/server/
# Linux / macOS
make generate && make wire && make build

# 运行(默认 MySQL/Redis/Kafka/RabbitMQ 均为 enabled:false,无需依赖即可启动)
./bin/server -conf=./config
# 使用 kratos 工具运行(推荐,自动处理工作目录和热重载)
kratos run -- -conf ./configs -e local
kratos run -- -conf ./configs -e prod

# 直接运行二进制
./bin/server -conf=./configs

# 指定环境配置(-e 或 --env,加载 configs/{env}/config.yaml)
./bin/server -e local
./bin/server -e prod

# CLI 定时任务
go build -o bin/task ./cmd/task/
./bin/task savePageDataCron -conf=./config

# 交互命令(cobra CLI,支持 -conf/-c 指定配置目录,-e 指定环境)
./bin/task help # 查看帮助
./bin/task # 默认执行 savePageData
./bin/task savePageData # 爬数据入库(单次)
./bin/task savePageData -e local # 使用 local 环境配置
./bin/task savePageDataCron # 爬数据入库(单次运行)
./bin/task savePageDataCron --daemon # 按 cron 循环运行
./bin/task savePageDataCron -e prod --daemon # 生产环境循环爬取

# 使用 kratos run 运行 task
kratos run -- -conf ./configs -e local savePageData
kratos run -- -conf ./configs -e prod savePageDataCron --daemon
```

默认 HTTP 端口 `8989`(与原 Gin 项目一致),Hello 接口:`GET http://127.0.0.1:8989/test`
Expand All @@ -108,7 +132,7 @@ go build -o bin/task ./cmd/task/

```bash
docker compose up -d
cp config/config.example.yaml config/config.yaml # 按需修改 enabled: true
cp configs/config.example.yaml configs/config.yaml # 按需修改 enabled: true
```

### 6. OpenAPI
Expand Down Expand Up @@ -140,7 +164,7 @@ $env:INTEGRATION_LEAVE_UP = "1"
powershell -File scripts/integration-test.ps1
```

集成测试使用独立配置目录 `config/integration/`(`kafka` / `rabbitmq` 均为 `enabled: true`),构建标签为 `integration`,日常 `go test ./...` 不会执行。
集成测试使用独立配置目录 `configs/integration/`(`kafka` / `rabbitmq` 均为 `enabled: true`),构建标签为 `integration`,日常 `go test ./...` 不会执行。

### 8. 部署

Expand All @@ -158,7 +182,29 @@ docker run -p 8000:8000 -p 9000:9000 kratos-develop-template:latest

## 配置说明

编辑 `config/config.yaml`:
### 环境配置

项目支持多环境配置,通过 `-e` 或 `--env` 指定:

| 环境 | 配置目录 | 说明 |
|------|----------|------|
| `local` | `configs/local/` | 本地开发(默认) |
| `bvt` | `configs/bvt/` | 构建验证测试 |
| `test` | `configs/test/` | 测试环境 |
| `prod` | `configs/prod/` | 生产环境 |
| `integration` | `configs/integration/` | 集成测试(kafka/rabbitmq enabled) |

```bash
# 加载指定环境配置
./bin/server -e local
./bin/server -e prod
./bin/task -e local savePageData
./bin/task -c ./configs -e test savePageDataCron --daemon
```

### 配置项示例

编辑 `configs/config.yaml` 或对应环境的配置:

```yaml
server:
Expand Down
4 changes: 2 additions & 2 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ var (
)

func init() {
flag.StringVar(&Flagconfig, "conf", "./config", "config directory, eg: -conf ./config")
flag.StringVar(&Flagenv, "e", "", "env shorthand: local, prod, integration ??config/{env}")
flag.StringVar(&Flagconfig, "conf", "./configs", "config directory, eg: -conf ./configs")
flag.StringVar(&Flagenv, "e", "", "env shorthand: local, prod, integration config/{env}")
}

func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server) *kratos.App {
Expand Down
78 changes: 57 additions & 21 deletions cmd/task/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"flag"
"fmt"
"os"

Expand All @@ -14,46 +13,83 @@ import (
"github.com/go-kratos/kratos/v2/log"
)

var (
flagconf string
flagenv string
)

func init() {
flag.StringVar(&flagconf, "conf", "./config", "config directory, eg: -conf ./config")
flag.StringVar(&flagenv, "e", "", "env shorthand (Gin parity): local, prod, integration ??config/{env}")
// Register flags on rootCmd so cobra recognizes them
rootCmd.PersistentFlags().StringVarP(&flagconf, "conf", "c", "./configs", "config directory, eg: -conf ./configs")
rootCmd.PersistentFlags().StringVarP(&flagenv, "e", "e", "", "env shorthand (Gin parity): local, prod, integration → config/{env}")
rootCmd.PersistentFlags().StringVar(&flagenv, "env", "", "alias for -e")

// Register subcommands
rootCmd.AddCommand(newSavePageDataCmd())
rootCmd.AddCommand(newSavePageDataCronCmd())
}

func main() {
flag.Parse()
// Normalize go-style flags to cobra format before cobra parses.
// Cobra treats -conf as -c + "onfig", so we convert -conf → --conf upfront.
args := make([]string, 0, len(os.Args)-1)
for i := 1; i < len(os.Args); i++ {
arg := os.Args[i]
if arg == "-conf" {
arg = "--conf"
}
args = append(args, arg)
}

// If no subcommand provided, default to savePageData
hasSubcommand := false
for _, arg := range args {
if arg == "savePageData" || arg == "savePageDataCron" || arg == "completion" || arg == "help" {
hasSubcommand = true
break
}
}
if !hasSubcommand {
args = append(args, "savePageData")
}

os.Args = append([]string{os.Args[0]}, args...)

if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}

// loadBootstrap loads config and returns bootstrap + cleanup function.
// Subcommands should call this in their RunE.
func loadBootstrap() (*conf.Bootstrap, func(), error) {
confDir := confpath.Resolve(flagconf, flagenv)
if flagenv != "" {
fmt.Printf("using config env %q ??%s\n", flagenv, confDir)
fmt.Printf("using config env %q %s\n", flagenv, confDir)
}

logger := log.NewStdLogger(os.Stdout)
c := config.New(config.WithSource(file.NewSource(confDir)))
defer c.Close()
if err := c.Load(); err != nil {
panic(err)
c.Close()
return nil, nil, err
}
var bc conf.Bootstrap
if err := c.Scan(&bc); err != nil {
panic(err)
c.Close()
return nil, nil, err
}

cleanup, err := initTaskApp(bc.GetData(), logger)
if err != nil {
panic(err)
c.Close()
return nil, nil, err
}
defer cleanup()

crawlerUC := biz.NewCrawlerUsecase(bc.GetApp())
rootCmd.AddCommand(newSavePageDataCmd(crawlerUC))
rootCmd.AddCommand(newSavePageDataCronCmd(crawlerUC))

if err := rootCmd.Execute(); err != nil {
os.Exit(1)
fullCleanup := func() {
cleanup()
c.Close()
}

return &bc, fullCleanup, nil
}

// createCrawlerUsecase is called by subcommands after loading config.
func createCrawlerUsecase(bc *conf.Bootstrap) *biz.CrawlerUsecase {
return biz.NewCrawlerUsecase(bc.GetApp())
}
1 change: 1 addition & 0 deletions cmd/task/output/outputGetAllTagsData.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
complete the task GetAllTagsData at 2026-06-04T23:52:58+08:00
19 changes: 11 additions & 8 deletions cmd/task/root.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package main

import (
"github.com/spf13/cobra"
)
import "github.com/spf13/cobra"

var (
flagconf string
flagenv string

var rootCmd = &cobra.Command{
Use: "kratos-task",
Short: "Kratos develop template CLI tasks",
Long: "Scheduled / batch tasks migrated from gin-develop-template.",
}
rootCmd = &cobra.Command{
Use: "kratos-task",
Short: "Kratos develop template CLI tasks",
Long: "Scheduled / batch tasks migrated from gin-develop-template.",
}
)
11 changes: 8 additions & 3 deletions cmd/task/save_page_data.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package main

import (
"github.com/JasonMetal/kratos-develop-template/internal/biz"

"github.com/spf13/cobra"
)

func newSavePageDataCmd(uc *biz.CrawlerUsecase) *cobra.Command {
func newSavePageDataCmd() *cobra.Command {
return &cobra.Command{
Use: "savePageData",
Short: "爬数据入库(单次 GetAllTagsData)",
Long: "与 gin-develop-template 的 savePageData 一致:只跑 GetAllTagsData,不执行 shell/cron",
RunE: func(cmd *cobra.Command, args []string) error {
bc, cleanup, err := loadBootstrap()
if err != nil {
return err
}
defer cleanup()

uc := createCrawlerUsecase(bc)
return uc.RunSavePageData(cmd.Context())
},
}
Expand Down
11 changes: 8 additions & 3 deletions cmd/task/save_page_data_cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@ import (
"os/signal"
"syscall"

"github.com/JasonMetal/kratos-develop-template/internal/biz"

"github.com/spf13/cobra"
)

func newSavePageDataCronCmd(uc *biz.CrawlerUsecase) *cobra.Command {
func newSavePageDataCronCmd() *cobra.Command {
var daemon bool
cmd := &cobra.Command{
Use: "savePageDataCron",
Short: "爬数据入库",
Long: "抓取并入库(单次运行)。--daemon 与 gin crawlerLogic 一样按 cron 循环",
RunE: func(cmd *cobra.Command, args []string) error {
bc, cleanup, err := loadBootstrap()
if err != nil {
return err
}
defer cleanup()

uc := createCrawlerUsecase(bc)
if daemon {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
Expand Down
52 changes: 52 additions & 0 deletions configs/bvt/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# BVT env (Gin: -e bvt → config/bvt/)
# Run: go run ./cmd/server/ -e bvt

server:
http:
addr: 0.0.0.0:8989
timeout: 15s
grpc:
addr: 0.0.0.0:50052
timeout: 15s
data:
database:
enabled: false
driver: mysql
source: root:root@tcp(127.0.0.1:3306)/gin_db?charset=utf8&parseTime=True&loc=Local&timeout=10s
max_idle_conns: 10
max_open_conns: 100
conn_max_lifetime: 3600s
redis:
enabled: false
addr: 127.0.0.1:6379
password: ""
db: 0
dial_timeout_seconds: 5
read_timeout_seconds: 3
write_timeout_seconds: 3
pool_size: 50
min_idle_conns: 10
kafka:
enabled: false
brokers:
- 127.0.0.1:9092
rabbitmq:
enabled: false
url: amqp://guest:guest@127.0.0.1:5672/
exchange: bvt_exchange
exchange_type: direct
routing_key: bvt_routing_key
durable: true
app:
auth:
domain_mark: ""
auth_token: "foobar386cb6d14ab7b5f6738f7fc1704cto"
private_key_path: config/certs/privateKey.pem
public_key_path: config/certs/publicKey.pem
rsa:
certs_dir: config/certs
key_bits: 2048
crawler:
shell_script: scripts/crawler.sh
output_dir: output
api_host: http://127.0.0.1:8888
File renamed without changes.
Loading
Loading