Files
juwan-backend/docs/PROJECT_GUIDE.md
zetaloop 5348966633 fix: 调整 chat WS/WT dev 接入
WT 目前沿用 JToken 的 JWT 校验;撤销一致性留到后续 WT 专用网关设计。
2026-04-25 06:54:00 +08:00

1033 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Juwan 后端项目完整使用指南
## 项目概述
```
Juwan 是一个基于 Go-Zero 微服务框架的分布式后端系统,采用以下架构:
┌─────────────────────────────────────────────────────────────────────┐
│ Envoy Gateway (负载均衡、认证) │
│ 端口: 80 (HTTP) │
└──────────────┬──────────────────────────────────────────────────────┘
┌───────┴──────────┐
│ │
┌───▼────────┐ ┌───▼────────┐
│ User API │ │ Order API │
│ (8888) │ │ (8888) │
└───┬────────┘ └────────────┘
┌───▼────────────────────┐
│ User RPC (内部使用) │
│ gRPC (不暴露) │
└────────────────────────┘
┌───▼────────────────────┐
│ PostgreSQL Database │
└────────────────────────┘
```
**关键特性:**
- ✅ API 层通过 Envoy Gateway 暴露给外部
- ✅ RPC 层仅限集群内部通信(通过 K8s Service Discovery
- ✅ JWT 认证(可选路由)
- ✅ 密码加密存储
- ✅ 用户会话管理
---
## 1️⃣ 添加新服务(完整步骤)
### 示例:添加一个 Product API 服务
#### Step 1: 定义 API 接口
创建 `desc/api/product.api`
```goctl
syntax = "v1"
type (
Product {
ProductId int64 `json:"productId"`
Name string `json:"name"`
Description string `json:"description"`
Price float64 `json:"price"`
Stock int32 `json:"stock"`
CreateAt int64 `json:"createAt"`
}
CreateProductReq {
Name string `json:"name" binding:"required,min=2"`
Description string `json:"description"`
Price float64 `json:"price" binding:"required,gt=0"`
Stock int32 `json:"stock" binding:"required,gte=0"`
}
CreateProductResp {
ProductId int64 `json:"productId"`
Message string `json:"message"`
}
GetProductReq {
ProductId int64 `path:"productId" binding:"required,gt=0"`
}
ListProductsReq {
Page int64 `form:"page" binding:"required,gt=0"`
Limit int64 `form:"limit" binding:"required,gt=0,lte=100"`
}
ListProductsResp {
Total int64 `json:"total"`
Products []Product `json:"products"`
}
)
@server (
group: product
prefix: /api/products
middleware: Logger
)
service product-api {
@doc (summary: "创建商品")
@handler CreateProduct
post / (CreateProductReq) returns (CreateProductResp)
@doc (summary: "获取商品详情")
@handler GetProduct
get /:productId (GetProductReq) returns (Product)
@doc (summary: "列出所有商品")
@handler ListProducts
get / (ListProductsReq) returns (ListProductsResp)
}
```
#### Step 2: 创建 RPC 定义(内部服务)
创建 `desc/rpc/product.proto`
```proto
syntax = "proto3";
option go_package = "./pb";
package pb;
message Product {
int64 productId = 1;
string name = 2;
string description = 3;
double price = 4;
int32 stock = 5;
int64 createAt = 6;
}
message GetProductReq {
int64 productId = 1;
}
message GetProductResp {
Product product = 1;
}
message UpdateStockReq {
int64 productId = 1;
int32 delta = 2; // 正数增加库存,负数减少
}
message UpdateStockResp {
int32 newStock = 1;
}
service ProductCenter {
rpc GetProduct(GetProductReq) returns (GetProductResp);
rpc UpdateStock(UpdateStockReq) returns (UpdateStockResp);
}
```
#### Step 3: 生成代码
```bash
# 生成 API 服务代码
goctl api go -api desc/api/product.api -dir app/product/api --style=goZero
# 生成 RPC 服务代码
goctl rpc protoc desc/rpc/product.proto \
--go_out=./app/product/rpc \
--go-grpc_out=./app/product/rpc \
--zrpc_out=./app/product/rpc \
--style=goZero
```
#### Step 4: 实现业务逻辑
编辑 `app/product/api/internal/logic/product/createproduct.go`
```go
package product
import (
"context"
"product-api/app/product/api/internal/svc"
"product-api/app/product/api/internal/types"
)
type CreateProductLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateProductLogic {
return &CreateProductLogic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateProductLogic) CreateProduct(req *types.CreateProductReq) (*types.CreateProductResp, error) {
// TODO: 调用 RPC 或数据库创建商品
return &types.CreateProductResp{
ProductId: 1,
Message: "创建成功",
}, nil
}
```
#### Step 5: 配置 K8s 部署
创建 `deploy/k8s/service/product/product-api.yaml`
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: product-api-config
namespace: juwan
data:
product-api.yaml: |
Name: product-api
Host: 0.0.0.0
Port: 8890
Database:
DataSource: postgres://user:pass@pg-dx:5432/juwan
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-api
namespace: juwan
spec:
replicas: 2
selector:
matchLabels:
app: product-api
template:
metadata:
labels:
app: product-api
spec:
containers:
- name: product-api
image: your-registry/product-api:latest
ports:
- containerPort: 8890
volumeMounts:
- name: config
mountPath: /etc/product-api
env:
- name: TZ
value: "Asia/Shanghai"
volumes:
- name: config
configMap:
name: product-api-config
---
apiVersion: v1
kind: Service
metadata:
name: product-api-svc
namespace: juwan
spec:
selector:
app: product-api
ports:
- port: 8890
targetPort: 8890
type: ClusterIP
```
#### Step 6: 更新 Envoy 网关配置
`deploy/k8s/envoy-gateway.yaml` 中添加:
```yaml
# 在 http_filters->route_config->virtual_hosts->routes 添加:
- match:
prefix: /api/products
route:
cluster: product_api_cluster
timeout: 30s
# 在 clusters 添加:
- name: product_api_cluster
connect_timeout: 5s
type: STRICT_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: product_api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: product-api-svc.juwan.svc.cluster.local
port_value: 8890
health_checks:
- timeout: 3s
interval: 10s
unhealthy_threshold: 2
healthy_threshold: 2
http_health_check:
path: /health
```
#### Step 7: 部署到集群
```bash
# 应用 K8s 配置
kubectl apply -f deploy/k8s/service/product/product-api.yaml
# 更新 Envoy Gateway 配置
kubectl apply -f deploy/k8s/envoy-gateway.yaml
# 重启 Envoy Pod 使配置生效
kubectl delete pods -n juwan -l app=envoy-gateway
# 验证
kubectl get pods -n juwan
curl http://localhost/api/products
```
---
## 2️⃣ RPC 服务内部隔离(不暴露给外部)
### 当前架构(推荐)
```
┌────────────────────────────────────────────┐
│ 外部客户端 (互联网) │
└────────────┬─────────────────────────────┘
┌────▼──────────┐
│ Envoy Gateway│
│ (仅 HTTP) │
└────┬──────────┘
┌────────┴─────────────┐
│ │
┌───▼───────────┐ ┌──────▼─────────┐
│ User API │ │ Product API │
│ (8888) │ │ (8890) │
└───┬───────────┘ └────────────────┘
┌───▼──────────────────────────────────┐
│ User RPC (完全隐藏) │
│ - 不暴露端口 │
│ - gRPC 通信 │
│ - K8s Service DNS 发现 │
│ - NetworkPolicy 限制通信 │
└──────────────────────────────────────┘
```
### 实现步骤
#### 1. 创建仅内部的 Service(无 port 暴露)
`deploy/k8s/service/user/user-rpc.yaml`
```yaml
apiVersion: v1
kind: Service
metadata:
name: user-rpc-svc
namespace: juwan
spec:
selector:
app: user-rpc
ports:
- name: grpc
port: 50051
targetPort: 50051
type: ClusterIP # 📌 仅限集群内部访问,不暴露 NodePort 或 LoadBalancer
sessionAffinity: None
```
#### 2. 配置 NetworkPolicy(进一步限制)
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: user-rpc-access
namespace: juwan
spec:
podSelector:
matchLabels:
app: user-rpc
policyTypes:
- Ingress
ingress:
# 仅允许 API 和其他服务(如 Order API)访问
- from:
- podSelector:
matchLabels:
app: user-api
- podSelector:
matchLabels:
app: order-api
- podSelector:
matchLabels:
app: product-api
ports:
- protocol: TCP
port: 50051
```
#### 3. API 服务中调用 RPC
`app/users/api/internal/svc/servicecontext.go`
```go
package svc
import (
"github.com/zeromicro/go-zero/zrpc"
"app/users/api/internal/config"
"app/users/rpc/pb"
)
type ServiceContext struct {
Config config.Config
UserRpc pb.UsercenterClient // RPC 客户端
}
func NewServiceContext(c config.Config) *ServiceContext {
userRpcClient := zrpc.MustNewClient(c.UserRpc)
return &ServiceContext{
Config: c,
UserRpc: pb.NewUsercenterClient(userRpcClient.Conn()),
}
}
```
配置文件 `app/users/api/etc/user-api.yaml`
```yaml
Name: user-api
Host: 0.0.0.0
Port: 8888
# RPC 配置(使用 K8s DNS
UserRpc:
Endpoints:
- user-rpc-svc.juwan.svc.cluster.local:50051
Database:
DataSource: postgres://user:pass@pg-dx:5432/juwan
```
#### 4. RPC 不在 Envoy 中配置路由
❌ **不要**在 `envoy-gateway.yaml` 中添加 RPC 集群:
```yaml
# ❌❌❌ 不要这样做 ❌❌❌
# routes:
# - match:
# prefix: /juwan.pb.Usercenter/
# route:
# cluster: user_rpc_cluster # 这样会暴露 RPC
```
---
## 3️⃣ JWT 认证与分级访问控制
### 实现逻辑
```
请求到达 Envoy → JWT 验证
┌──────────────────────────────────────┐
│ 无效或缺省 Token │
└──────────┬───────────────────────────┘
公开路由 (允许) ──→ /api/users/login
/api/users/register
/api/products (列表、详情)
受保护路由 (拒绝) ──→ 返回 401 Unauthorized
┌──────▼───────────────────────────┐
│ 有效 Token │
│ (已登录) │
└──────┬───────────────────────────┘
┌──────▼──────────────────────────────────────────────┐
│ 完整数据访问 (取决于后端 RPC) │
│ - 用户信息 (包括隐私信息) │
│ - 订单历史 │
│ - 收藏列表 │
└──────────────────────────────────────────────────────┘
```
### 配置步骤
#### 1. 生成 JWT 密钥并存储为 K8s Secret
```bash
# 生成 HMAC 密钥(用于签名)
openssl rand -hex 32
# 输出示例: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
# 创建 Secret
kubectl create secret generic jwt-secret \
--from-literal=key=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 \
-n juwan
# 验证
kubectl get secret jwt-secret -n juwan -o yaml
```
#### 2. 更新 Envoy 配置(添加 JWT 验证)
编辑 `deploy/k8s/envoy-gateway.yaml`
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: envoy-config
namespace: juwan
data:
envoy.yaml: |
static_resources:
listeners:
- name: listener_http
address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
http_filters:
# JWT 认证过滤器(在 router 之前)
- name: envoy.filters.http.jwt_authn
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
# 定义 JWT 提供者
providers:
my-provider:
issuer: "juwan"
audiences: "api"
# 使用文件系统上的密钥
local_jwks:
filename: /etc/envoy/jwks.json
# 定义受保护的路由
rules:
# 规则1: 登录和注册不需要认证
- match:
prefix: /api/users/login
allow_missing_or_failed: true # 允许缺省/失败的 token
- match:
prefix: /api/users/register
allow_missing_or_failed: true
# 规则2: 重定向认证失败请求
- match:
prefix: "/"
requires:
provider_name: "my-provider"
# 如果认证失败,Envoy 直接拒绝,返回 401
# 路由过滤器
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match:
prefix: /api/users
route:
cluster: user_api_cluster
- match:
prefix: /api/products
route:
cluster: product_api_cluster
# ... 其他路由 ...
# ... clusters 定义保持不变 ...
```
#### 3. 在 API 服务中添加认证中间件
创建 `app/users/api/internal/middleware/authmiddleware.go`
```go
package middleware
import (
"fmt"
"net/http"
"strings"
"github.com/golang-jwt/jwt/v4"
)
type AuthMiddleware struct {
JwtSecret string
}
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 获取 Authorization header
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
// 如果认证失败,Envoy 会返回 401,
// 但我们可以在 API 层添加自定义逻辑
next(w, r)
return
}
parts := strings.Split(authHeader, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
http.Error(w, "Invalid authorization header", http.StatusUnauthorized)
return
}
token := parts[1]
// 验证 token
_, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(m.JwtSecret), nil
})
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Token 有效,继续处理
next(w, r)
}
}
```
#### 4. 登录端点返回 JWT Token
编辑 `app/users/api/internal/logic/user/loginlogic.go`
```go
package user
import (
"context"
"time"
"github.com/golang-jwt/jwt/v4"
"app/users/api/internal/svc"
"app/users/api/internal/types"
)
type LoginLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func (l *LoginLogic) Login(req *types.LoginReq) (*types.LoginResp, error) {
// TODO: 验证用户名和密码
// 生成 JWT Token
claims := jwt.MapClaims{
"userId": 1, // 实际应从数据库获取
"username": req.Username,
"exp": time.Now().Add(24 * time.Hour).Unix(),
"iat": time.Now().Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(l.svcCtx.Config.JwtSecret))
if err != nil {
return nil, err
}
return &types.LoginResp{
Token: tokenString,
Expires: time.Now().Add(24 * time.Hour).Unix(),
}, nil
}
```
### JWT 认证时的分级访问
**后端 RPC 可处理分级访问:**
```go
// 在 User RPC 中实现
func (s *UsercenterServer) GetUserInfo(ctx context.Context, req *pb.GetUsersByIdReq) (*pb.GetUsersByIdResp, error) {
// 获取请求者的 userId(从 context 中取,由 API 层传递)
requesterID := ctx.Value("userId").(int64)
targetID := req.Id
// 查询用户信息
user := s.getUserFromDB(targetID)
if requesterID == targetID {
// 自己查看自己 → 返回完整信息(包含隐私信息)
return &pb.GetUsersByIdResp{
Users: &pb.Users{
UserId: user.UserId,
Username: user.Username,
Email: user.Email, // ✅ 包含
Phone: user.Phone, // ✅ 包含
// ... 所有字段
},
}, nil
} else {
// 查看别人 → 返回部分信息
return &pb.GetUsersByIdResp{
Users: &pb.Users{
UserId: user.UserId,
Username: user.Username,
// ❌ 不返回 Email、Phone 等隐私信息
},
}, nil
}
}
```
**或在 API 层处理:**
```go
func (l *GetUserInfoLogic) GetUserInfo(req *types.GetUserInfoReq) (*types.UserInfo, error) {
// 从 context 获取当前认证用户
currentUser := l.ctx.Value("userId").(int64)
// 调用 RPC
rpcResp, err := l.svcCtx.UserRpc.GetUsersById(l.ctx, &pb.GetUsersByIdReq{
Id: req.UserId,
})
if currentUser == req.UserId {
// 自己查看自己 → 返回所有信息
return &types.UserInfo{
UserId: rpcResp.Users.UserId,
Username: rpcResp.Users.Username,
Email: rpcResp.Users.Email,
Phone: rpcResp.Users.Phone,
}, nil
} else {
// 查看别人 → 仅返回公开信息
return &types.UserInfo{
UserId: rpcResp.Users.UserId,
Username: rpcResp.Users.Username,
}, nil
}
}
```
---
## 4️⃣ 认证失败处理策略
### 当前配置
| 路由 | 认证要求 | 认证失败 | 示例 |
|-----|---------|--------|------|
| `/api/users/login` | ❌ 不需要 | 放行 | `POST /api/users/login` (允许) |
| `/api/users/register` | ❌ 不需要 | 放行 | `POST /api/users/register` (允许) |
| `/api/users/:userId` | ✅ 需要 | 拒绝 401 | `GET /api/users/123` (无 token → 401) |
| `/api/users/:userId/password` | ✅ 需要 | 拒绝 401 | `PUT /api/users/123/password` (无 token → 401) |
| `/api/products` | ❌ 不需要 | 放行 | `GET /api/products` (允许) |
| `/api/products/:id` | ❌ 不需要 | 放行 | `GET /api/products/1` (允许) |
### 自定义错误响应
创建 `deploy/k8s/envoy-gateway.yaml` 的自定义拒绝响应:
```yaml
# 在 jwt_authn 过滤器中配置
http_filters:
- name: envoy.filters.http.jwt_authn
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
providers:
my-provider:
issuer: "juwan"
audiences: "api"
local_jwks:
filename: /etc/envoy/jwks.json
rules:
- match:
prefix: /api/users/login
allow_missing_or_failed: true
- match:
prefix: /api/users/register
allow_missing_or_failed: true
- match:
prefix: /
requires:
provider_name: "my-provider"
# 认证失败后的行为
bypass_cors_preflight: false # 允许 OPTIONS 跨域请求
```
**认证失败时 Envoy 返回 401**
```json
HTTP/1.1 401 Unauthorized
content-type: text/html
Jwt verification fails.
```
### 在 API 层添加自定义错误处理
如果希望自定义错误响应,可以在每个受保护的 handler 中添加检查:
```go
func (l *UpdateUserInfoLogic) UpdateUserInfo(req *types.UpdateUserInfoReq) (*types.UpdateUserInfoResp, error) {
// 获取当前请求者的身份
ctx := context.WithValue(l.ctx, "currentUserId", req.UserId)
// 如果没有匹配的 token,API 层可以添加自定义错误
if req.UserId <= 0 {
return nil, errors.New("invalid user id")
}
// 继续处理
return &types.UpdateUserInfoResp{Message: "更新成功"}, nil
}
```
---
## 5️⃣ 完整工作流示例
### 场景:用户注册 → 登录 → 获取用户信息
#### 步骤 1: 注册用户(无需认证)
```bash
curl -X POST http://localhost/api/users/register \
-H "Content-Type: application/json" \
-d '{
"username": "john_doe",
"password": "securePass123",
"email": "john@example.com",
"phone": "13800138001"
}'
# 响应
{
"userId": 1,
"username": "john_doe",
"email": "john@example.com",
"message": "用户注册成功"
}
```
#### 步骤 2: 登录获取 Token(无需认证)
```bash
curl -X POST http://localhost/api/users/login \
-H "Content-Type: application/json" \
-d '{
"username": "john_doe",
"password": "securePass123"
}'
# 响应
{
"userId": 1,
"username": "john_doe",
"email": "john@example.com",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires": 1708694400
}
```
#### 步骤 3: 获取用户信息(需要 Token)
```bash
# ❌ 无 Token → 401 Unauthorized
curl http://localhost/api/users/1
# Jwt verification fails.
# ✅ 有 Token → 成功
curl http://localhost/api/users/1 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# 响应
{
"userId": 1,
"username": "john_doe",
"email": "john@example.com",
"phone": "13800138001",
"avatar": "https://...",
"status": 1,
"createAt": 1708608000,
"updateAt": 1708608000
}
```
#### 步骤 4: 查看他人信息(部分数据)
```bash
# 用户1 查看用户2 的信息(已登录)
curl http://localhost/api/users/2 \
-H "Authorization: Bearer eyJhbGc..."
# 响应(仅公开信息)
{
"userId": 2,
"username": "jane_doe",
# ❌ 不包含 email, phone 等隐私信息
}
```
---
## 6️⃣ 部署检查清单
在部署新服务或更新配置前,使用此清单:
- [ ] **API 定义** - `desc/api/*.api` 文件已创建
- [ ] **RPC 定义** - `desc/rpc/*.proto` 文件已创建(如需内部通信)
- [ ] **代码生成** - 运行 `goctl api/rpc` 命令生成代码
- [ ] **业务逻辑** - 编辑 `app/*/api/internal/logic/` 实现功能
- [ ] **K8s 部署清单** - 创建 `deploy/k8s/service/*/` 文件
- [ ] ConfigMap(配置)
- [ ] Deployment(部署)
- [ ] ServiceK8s 服务发现)
- [ ] NetworkPolicy(网络隔离,可选)
- [ ] **Envoy 更新** - 修改 `deploy/k8s/envoy-gateway.yaml`
- [ ] 添加路由规则
- [ ] 添加上游集群
- [ ] 验证健康检查地址
- [ ] **测试验证**
```bash
kubectl apply -f deploy/k8s/service/{name}/
kubectl apply -f deploy/k8s/envoy-gateway.yaml
kubectl delete pods -n juwan -l app=envoy-gateway
curl http://localhost/api/{path} # 端口转发后测试
```
---
## 7️⃣ 故障排查
### 问题 1: 新服务无法访问
```bash
# 检查 Pod 状态
kubectl get pods -n juwan
# 查看 Pod 日志
kubectl logs -n juwan -l app=your-service --tail=100
# 检查 Service
kubectl get svc -n juwan
# 测试 DNS 解析
kubectl exec -it {pod-name} -n juwan -- nslookup your-service-svc.juwan.svc.cluster.local
```
### 问题 2: Envoy 配置错误
```bash
# 查看 Envoy Pod 日志
kubectl logs -n juwan -l app=envoy-gateway --tail=50
# 常见错误
# - "no such field" → YAML 字段名与 Envoy 版本不兼容
# - "Unknown cluster" → Envoy 配置中缺少 cluster 定义
# - "Connection refused" → 后端服务未启动或 DNS 无法解析
```
### 问题 3: JWT 认证失败
```bash
# 检查 JWT 配置是否正确
kubectl get configmap envoy-config -n juwan -o yaml
# 查看 jwks.json 是否存在
kubectl exec -it {envoy-pod} -n juwan -- cat /etc/envoy/jwks.json
# 验证 Token 格式
curl -H "Authorization: Bearer {token}" http://localhost/api/users/1
```
---
## 文件组织总结
```
project-root/
├── desc/ # 接口定义
│ ├── api/
│ │ ├── users.api # User API 定义
│ │ └── product.api # ← 新增:Product API 定义
│ ├── rpc/
│ │ ├── users.proto # User RPC 定义
│ │ └── product.proto # ← 新增:Product RPC 定义
│ └── sql/
├── app/ # 应用代码
│ ├── users/
│ │ ├── api/ # User API 实现
│ │ └── rpc/ # User RPC 实现(内部)
│ └── product/ # ← 新增:Product 服务
│ ├── api/
│ └── rpc/
├── deploy/ # K8s 部署
│ ├── k8s/
│ │ ├── envoy-gateway.yaml # ← 更新:添加新路由
│ │ ├── base/
│ │ └── service/
│ │ ├── user/
│ │ │ ├── user-api.yaml
│ │ │ └── user-rpc.yaml
│ │ └── product/ # ← 新增:Product 部署文件
│ │ ├── product-api.yaml
│ │ └── product-rpc.yaml
│ └── envoy/ # 参考配置
└── docs/ # 文档
└── PROJECT_GUIDE.md # ← 本文件
```
---
希望这份指南能帮助你快速上手项目!有任何问题欢迎提出 📝