当前位置: 首页 > wzjs >正文

做一个团购网站的成本拼多多关键词怎么优化

做一个团购网站的成本,拼多多关键词怎么优化,泰州网站建设服务热线,官网苹果手机14结合之前的基础,我们这里给出自己的实践环节。先给出完整的步骤 创建项目目录结构。初始化 Go 模块。添加必要的依赖。编写 config.yaml 配置文件。定义 Proto 文件并编译。定义 Model。实现 Repository 层。实现 Service 层。实现 Handler 层。实现 Server 层。实现…

结合之前的基础,我们这里给出自己的实践环节。先给出完整的步骤

  1. 创建项目目录结构。
  2. 初始化 Go 模块。
  3. 添加必要的依赖。
  4. 编写 config.yaml 配置文件。
  5. 定义 Proto 文件并编译。
  6. 定义 Model。
  7. 实现 Repository 层。
  8. 实现 Service 层。
  9. 实现 Handler 层。
  10. 实现 Server 层。
  11. 实现数据库初始化逻辑。
  12. 启动服务

创建项目目录结构

mkdir -p scaling-group-service/{api/proto/open/v1,cmd,config,internal/{model,repository,service,handler,server,utils},go.mod}

项目目录如下

scaling-group-service % tree
.
├── api                                     # 存放 gRPC 接口定义及相关生成代码
│   └── proto                               # Protocol Buffers 定义文件
│       └── open                            # 开放 API 命名空间(可理解为模块或分类)
│           └── v1                          # API 版本 v1
│               ├── scaling_group.pb.go     # 由 proto 编译生成的 Go 数据结构(pb)
│               ├── scaling_group.pb.gw.go  # gRPC-Gateway 生成的 HTTP 路由代理代码
│               ├── scaling_group.proto     # 原始的 gRPC 接口定义文件(proto3 格式)
│               └── scaling_group_grpc.pb.go# gRPC 服务接口和客户端的 Go 实现代码
├── cmd                                     # 命令行入口目录
│   └── main.go                             # 程序主入口,用于启动 gRPC 和 HTTP 服务
├── config                                  # 配置相关目录
│   ├── config.go                           # 配置加载逻辑(如读取 YAML 文件)
│   └── config.yml                          # YAML 格式的配置文件,包含数据库、端口等配置
├── go.mod                                  # Go 模块描述文件,定义模块路径和依赖
├── go.sum                                  # Go 模块依赖校验文件,记录依赖的哈希值
└── internal                                # 项目核心代码目录(Go 推荐使用 internal)├── handler                             # HTTP 请求处理器(适配 gRPC-Gateway)│   └── scaling_group_handler.go        # ScalingGroup 的 HTTP 请求处理逻辑├── model                               # 数据模型目录│   └── scaling_group.go                # ScalingGroup 的结构体定义,对应数据库表├── repository                          # 数据访问层(DAO),负责与数据库交互│   └── scaling_group_repository.go     # ScalingGroup 的数据库操作逻辑(如 CRUD)├── server                              # 服务启动逻辑目录│   ├── grpc_server.go                  # 启动 gRPC 服务,注册服务实现│   └── http_server.go                  # 启动 HTTP 服务(gRPC-Gateway),注册路由├── service                             # 业务逻辑层目录│   └── scaling_group_service.go        # ScalingGroup 的业务逻辑实现(gRPC 接口实现)└── utils                               # 工具类函数目录└── db.go                           # 数据库连接工具函数(如 PostgreSQL 初始化)

初始化go模块

cd scaling-group-service
go mod init scaling-group-service

生成一个go.mod文件,如下所示:

module scaling-group-servicego 1.23.4

添加依赖

我们需要添加一些必要的依赖,包括Viper, GORM和gRPC等,分别用于读取配置(数据库信息)文件、GORM数据库操作,gRPC等

go get github.com/spf13/viper
go get gorm.io/gorm
go get gorm.io/driver/postgres
go get google.golang.org/grpc
go get github.com/grpc-ecosystem/grpc-gateway/v2
go get google.golang.org/protobuf/cmd/protoc-gen-go
go get google.golang.org/grpc/cmd/protoc-gen-go-grpc

此时go.mod中新增如下内容
在这里插入图片描述

编写配置文件

config/config.yml文件

database:host: localhost # 我是本地启的dockerport: 5432user: postgrespassword: yourpassworddbname: scaling_group_dbsslmode: disabletimezone: Asia/Shanghaiserver:http_port: 8080grpc_port: 50051

其中config\config.go

package configimport ("fmt""github.com/spf13/viper"
)//type Config struct {
//	Database struct {
//		Host     string `yaml:"host"`
//		Port     int    `yaml:"port"`
//		User     string `yaml:"user"`
//		Password string `yaml:"password"`
//		DBName   string `yaml:"dbname"`
//		SSLMode  string `yaml:"sslmode"`
//		Timezone string `yaml:"timezone"`
//	} `yaml:"database"`
//
//	Server struct {
//		HTTPPort int `yaml:"http_port"`
//		GRPCPort int `yaml:"grpc_port"`
//	} `yaml:"server"`
//}type Config struct {Database struct {Host     string `yaml:"host"     mapstructure:"host"`Port     int    `yaml:"port"     mapstructure:"port"`User     string `yaml:"user"     mapstructure:"user"`Password string `yaml:"password" mapstructure:"password"`DBName   string `yaml:"dbname"   mapstructure:"dbname"`SSLMode  string `yaml:"sslmode"  mapstructure:"sslmode"`Timezone string `yaml:"timezone" mapstructure:"timezone"`} `yaml:"database" mapstructure:"database"`Server struct {HTTPPort int `yaml:"http_port" mapstructure:"http_port"`GRPCPort int `yaml:"grpc_port" mapstructure:"grpc_port"`} `yaml:"server" mapstructure:"server"`
}func LoadConfig(path string) (Config, error) {var cfg Configviper.AddConfigPath(path)viper.SetConfigName("config")viper.SetConfigType("yaml")viper.AutomaticEnv()if err := viper.ReadInConfig(); err != nil {return cfg, fmt.Errorf("failed to read config file: %w", err)}fmt.Printf("Raw config: %+v\n", viper.AllSettings())if err := viper.Unmarshal(&cfg); err != nil {return cfg, fmt.Errorf("unable to decode into struct: %w", err)}fmt.Printf("Loaded config: %+v\n", cfg)return cfg, nil
}

定义Proto文件

api/proto/open/v1/scaling_group.proto 中定义服务接口:

syntax = "proto3";package open.v1;option go_package = "scaling-group-service/api/proto/open/v1;v1";
import "google/api/annotations.proto";message ScalingGroup {string id = 1;string name = 2;int32 min_instance_count = 3;int32 max_instance_count = 4;string region = 5;string zone = 6;string status = 7;int64 created_at = 8;
}message CreateScalingGroupRequest {string name = 1;int32 min_instance_count = 2;int32 max_instance_count = 3;string region = 4;string zone = 5;
}message CreateScalingGroupResponse {string id = 1;
}message DeleteScalingGroupRequest {string id = 1;
}message DeleteScalingGroupResponse {}message DescribeScalingGroupRequest {string id = 1;
}message DescribeScalingGroupResponse {ScalingGroup group = 1;
}message ModifyScalingGroupRequest {string id = 1;optional int32 min_instance_count = 2;optional int32 max_instance_count = 3;optional string name = 4;
}message ModifyScalingGroupResponse {}service ScalingGroupService {rpc CreateScalingGroup(CreateScalingGroupRequest) returns (CreateScalingGroupResponse) {option (google.api.http) = {post: "/v1/scaling-groups"body: "*"};}rpc DeleteScalingGroup(DeleteScalingGroupRequest) returns (DeleteScalingGroupResponse) {option (google.api.http) = {delete: "/v1/scaling-groups/{id}"};}rpc DescribeScalingGroup(DescribeScalingGroupRequest) returns (DescribeScalingGroupResponse) {option (google.api.http) = {get: "/v1/scaling-groups/{id}"};}rpc ModifyScalingGroup(ModifyScalingGroupRequest) returns (ModifyScalingGroupResponse) {option (google.api.http) = {put: "/v1/scaling-groups/{id}"body: "*"};}
}    

为了生成 Go 代码,您需要使用 protoc 编译器以及相关的插件。首先,确保您已经安装了protoc和必要的插件。如果尚未安装,请参考以下命令进行安装:

ggo install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest

然后,在项目根目录下运行以下命令来编译 .proto 文件:

scaling-group-service % protoc \--go_out=. \--go_opt=paths=source_relative \--go-grpc_out=. \--go-grpc_opt=paths=source_relative \--grpc-gateway_out=. \--grpc-gateway_opt=paths=source_relative \--openapiv2_out=./swagger \--openapiv2_opt=logtostderr=true \api/proto/open/v1/scaling_group.proto

此时会报错如下:

google/api/annotations.proto: File not found.
launch_template.proto:5:1: Import "google/api/annotations.proto" was not found or had errors.

注意:我们使用了 google/api/annotations.proto,需要下载这个文件到本地或通过 proto import path解析
如果你遇到找不到 google/api/annotations.proto 的问题,可以克隆官方仓库:

mkdir -p third_party/google/api
curl -o third_party/google/api/annotations.proto https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto

然后编译时加上 -Ithird_party 参数。

或者使用

go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway@latest

之所以要下载这个,因为github.com\grpc-ecosystem\grpc-gateway@latest\third_party\googleapis\google\api目录下就有我们需要的annotations.proto文件。
执行上述下载命令之后,就会将protoc-gen-grpc-gateway下载到电脑的GOPATH下,自己电脑的GOPATH可以通过命令go env查看.

echo $GOPATH
/Users/zhiyu/go
dance@MacBook-Pro v2@v2.27.1 % cd /Users/zhiyu/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.14.5/third_party/googleapis 
dance@MacBook-Pro googleapis % ls
LICENSE                 README.grpc-gateway     google

我们引用本地的路劲,现在再次执行protoc

protoc \-I . \-I $(go env GOPATH)/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.16.0/third_party/googleapis \--go_out=. \--go_opt=paths=source_relative \--go-grpc_out=. \--go-grpc_opt=paths=source_relative \--grpc-gateway_out=. \--grpc-gateway_opt=paths=source_relative \api/proto/open/v1/scaling_group.proto

此时会在v1目录下生成两个文件

  1. api/proto/open/v1/scaling_group.pb.go: 包含消息类型和序列化逻辑。
  2. api/proto/open/v1/scaling_group_grpc.pb.go: 包含 gRPC 服务的客户端和服务器端接口。
  3. api/proto/open/v1/scaling_group.pb.gw.go: HTTP 路由绑定代码。

定义Model

internal/model/scaling_group.go 中定义模型:

package model// import "gorm.io/gorm"type ScalingGroup struct {ID               string `gorm:"primaryKey"`Name             stringMinInstanceCount int32MaxInstanceCount int32Region           stringZone             stringStatus           stringCreatedAt        int64
}

实现Repository层

internal/repository/scaling_group_repository.go 中实现数据访问层:

package repositoryimport ("context""errors""gorm.io/gorm""scaling-group-service/internal/model"
)type ScalingGroupRepository struct {db *gorm.DB
}func NewScalingGroupRepository(db *gorm.DB) *ScalingGroupRepository {return &ScalingGroupRepository{db: db}
}func (r *ScalingGroupRepository) Create(ctx context.Context, group *model.ScalingGroup) error {return r.db.WithContext(ctx).Create(group).Error
}func (r *ScalingGroupRepository) GetByID(ctx context.Context, id string) (*model.ScalingGroup, error) {var group model.ScalingGroupif err := r.db.WithContext(ctx).Where("id = ?", id).First(&group).Error; err != nil {if errors.Is(err, gorm.ErrRecordNotFound) {return nil, nil}return nil, err}return &group, nil
}func (r *ScalingGroupRepository) Update(ctx context.Context, group *model.ScalingGroup) error {return r.db.WithContext(ctx).Model(group).Where("id = ?", group.ID).Save(group).Error
}func (r *ScalingGroupRepository) Delete(ctx context.Context, id string) error {return r.db.WithContext(ctx).Where("id = ?", id).Delete(&model.ScalingGroup{}).Error
}

实现Service层

internal/service/scaling_group_service.go 中实现业务逻辑层:

package serviceimport ("context""fmt""scaling-group-service/api/proto/open/v1""scaling-group-service/internal/model""scaling-group-service/internal/repository""time"
)type ScalingGroupService struct {v1.UnimplementedScalingGroupServiceServerrepo *repository.ScalingGroupRepository
}func NewScalingGroupService(repo *repository.ScalingGroupRepository) *ScalingGroupService {return &ScalingGroupService{repo: repo,}
}func (s *ScalingGroupService) CreateScalingGroup(ctx context.Context, req *v1.CreateScalingGroupRequest) (*v1.CreateScalingGroupResponse, error) {// 修改这里:生成 asg- 开头的 IDid := fmt.Sprintf("asg-%d", time.Now().UnixNano())group := &model.ScalingGroup{ID:               id,Name:             req.Name,MinInstanceCount: req.MinInstanceCount,MaxInstanceCount: req.MaxInstanceCount,Region:           req.Region,Zone:             req.Zone,Status:           "active",CreatedAt:        time.Now().Unix(),}if err := s.repo.Create(ctx, group); err != nil {return nil, err}return &v1.CreateScalingGroupResponse{Id: group.ID}, nil
}func (s *ScalingGroupService) DeleteScalingGroup(ctx context.Context, req *v1.DeleteScalingGroupRequest) (*v1.DeleteScalingGroupResponse, error) {if err := s.repo.Delete(ctx, req.Id); err != nil {return nil, err}return &v1.DeleteScalingGroupResponse{}, nil
}func (s *ScalingGroupService) DescribeScalingGroup(ctx context.Context, req *v1.DescribeScalingGroupRequest) (*v1.DescribeScalingGroupResponse, error) {group, err := s.repo.GetByID(ctx, req.Id)if err != nil {return nil, err}if group == nil {return &v1.DescribeScalingGroupResponse{}, nil}return &v1.DescribeScalingGroupResponse{Group: &v1.ScalingGroup{Id:               group.ID,Name:             group.Name,MinInstanceCount: group.MinInstanceCount,MaxInstanceCount: group.MaxInstanceCount,Region:           group.Region,Zone:             group.Zone,Status:           group.Status,CreatedAt:        group.CreatedAt,},}, nil
}func (s *ScalingGroupService) ModifyScalingGroup(ctx context.Context, req *v1.ModifyScalingGroupRequest) (*v1.ModifyScalingGroupResponse, error) {existing, err := s.repo.GetByID(ctx, req.Id)if err != nil || existing == nil {return nil, fmt.Errorf("group not found")}if req.Name != nil {existing.Name = *req.Name}if req.MinInstanceCount != nil {existing.MinInstanceCount = *req.MinInstanceCount}if req.MaxInstanceCount != nil {existing.MaxInstanceCount = *req.MaxInstanceCount}if err := s.repo.Update(ctx, existing); err != nil {return nil, err}return &v1.ModifyScalingGroupResponse{}, nil
}

实现Server层

server/grpc_server.go

package serverimport ("fmt""google.golang.org/grpc""net""scaling-group-service/api/proto/open/v1""scaling-group-service/internal/service"
)func StartGRPCServer(grpcPort int, scalingGroupService *service.ScalingGroupService) error {lis, err := net.Listen("tcp", fmt.Sprintf(":%d", grpcPort))if err != nil {return err}grpcServer := grpc.NewServer()v1.RegisterScalingGroupServiceServer(grpcServer, scalingGroupService)fmt.Printf("gRPC server listening on port %d\n", grpcPort)return grpcServer.Serve(lis)
}

另一个是http_server.go

package serverimport ("context""fmt""google.golang.org/grpc""log""net/http""os""os/signal""time""github.com/grpc-ecosystem/grpc-gateway/v2/runtime""scaling-group-service/internal/handler"
)func StartHTTPServer(ctx context.Context, httpPort int, grpcAddr string) error {mux := runtime.NewServeMux()if err := handler.RegisterHandlers(ctx, mux, grpcAddr, []grpc.DialOption{grpc.WithInsecure()}); err != nil {return err}srv := &http.Server{Addr:    fmt.Sprintf(":%d", httpPort),Handler: mux,}go func() {if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("HTTP server ListenAndServe: %v", err)}}()fmt.Printf("HTTP server listening on port %d\n", httpPort)// Graceful shutdownstop := make(chan os.Signal, 1)signal.Notify(stop, os.Interrupt)<-stopctxShutDown, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer func() {cancel()}()if err := srv.Shutdown(ctxShutDown); err != nil {log.Fatalf("HTTP Server Shutdown failed: %v", err)}return nil
}

实现utils

db.go文件

package utilsimport ("fmt""gorm.io/driver/postgres""gorm.io/gorm""scaling-group-service/config""scaling-group-service/internal/model"
)func ConnectDB(cfg config.Config) (*gorm.DB, error) {dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",cfg.Database.Host,cfg.Database.Port,cfg.Database.User,cfg.Database.Password,cfg.Database.DBName,cfg.Database.SSLMode,cfg.Database.Timezone,)db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})if err != nil {return nil, err}err = db.AutoMigrate(&model.ScalingGroup{})if err != nil {return nil, err}return db, nil
}

handler

实现handler/scaling_group_handler.go

package handlerimport ("context""github.com/grpc-ecosystem/grpc-gateway/v2/runtime""google.golang.org/grpc""scaling-group-service/api/proto/open/v1"
)func RegisterHandlers(ctx context.Context, mux *runtime.ServeMux, grpcAddr string, opts []grpc.DialOption) error {conn, err := grpc.DialContext(ctx, grpcAddr, opts...)if err != nil {return err}//defer conn.Close()client := v1.NewScalingGroupServiceClient(conn)return v1.RegisterScalingGroupServiceHandlerClient(ctx, mux, client)
}

入口函数cmd/main

package mainimport ("context""fmt""log""sync""scaling-group-service/config""scaling-group-service/internal/repository""scaling-group-service/internal/server""scaling-group-service/internal/service""scaling-group-service/internal/utils"
)func main() {cfg, err := config.LoadConfig("./config")if err != nil {log.Fatalf("Load config error: %v", err)}fmt.Printf("Http port: %v\n", cfg.Server.HTTPPort)fmt.Printf("Http port: %v\n", cfg.Server.GRPCPort)db, err := utils.ConnectDB(cfg)if err != nil {log.Fatalf("Connect DB error: %v", err)}repo := repository.NewScalingGroupRepository(db)svc := service.NewScalingGroupService(repo)grpcPort := cfg.Server.GRPCPorthttpPort := cfg.Server.HTTPPortgrpcAddr := fmt.Sprintf("localhost:%d", grpcPort)var wg sync.WaitGroupwg.Add(2)go func() {defer wg.Done()if err := server.StartGRPCServer(grpcPort, svc); err != nil {log.Fatalf("Start GRPC server error: %v", err)}}()go func() {defer wg.Done()if err := server.StartHTTPServer(context.Background(), httpPort, grpcAddr); err != nil {log.Fatalf("Start HTTP server error: %v", err)}}()wg.Wait()
}

准备好之后,创建db
使用docker run 创建PostgreSQL

  1. 启动postgreslq

运行 PostgreSQL 容器

docker run --name scaling-db \-e POSTGRES_PASSWORD=yourpassword \-p 5432:5432 \-d postgres:14
  1. 创建库和表

创建数据库

docker exec scaling-db psql -U postgres -c "CREATE DATABASE scaling_group_db;"

注意这里的model结构如下

package model// import "gorm.io/gorm"type ScalingGroup struct {ID               string `gorm:"primaryKey"`Name             stringMinInstanceCount int32MaxInstanceCount int32Region           stringZone             stringStatus           stringCreatedAt        int64
}

所以创建表结构

# 创建表结构
docker exec scaling-db psql -U postgres -d scaling_group_db -c "
docker exec scaling-db psql -U postgres -d scaling_group_db -c "
CREATE TABLE IF NOT EXISTS scaling_groups (id VARCHAR(255) PRIMARY KEY,name VARCHAR(255),min_instance_count INT,max_instance_count INT,region VARCHAR(255),zone VARCHAR(255),status VARCHAR(255),created_at BIGINT
);
"
  1. 查看数据库表
# 列出所有数据库
docker exec scaling-db psql -U postgres -c "\l"
# 查看表结构
docker exec scaling-db psql -U postgres -d scaling_group_db -c "\d scaling_groups"

请求与测试

go run cmd/main.go

在这里插入图片描述

创建资源

curl -X POST http://localhost:8080/v1/scaling-groups \-H "Content-Type: application/json" \-d '{"name": "test-group","min_instance_count": 1,"max_instance_count": 3,"region": "cn-beijing","zone": "cn-beijing-1"}'

在这里插入图片描述

登录数据库查询

docker exec -it scaling-db psql -U postgres -d scaling_group_db

在这里插入图片描述

查询资源

curl http://localhost:8080/v1/scaling-groups/asg-1752661325196631000

修改资源

curl -X PUT http://localhost:8080/v1/scaling-groups/asg-1752661325196631000 \-H "Content-Type: application/json" \-d '{"min_instance_count": 2,"max_instance_count": 5,"name": "updated-name"}'

删除资源

curl -X DELETE http://localhost:8080/v1/scaling-groups/asg-1752661325196631000

Q&A

config/config.go中我们注释了如下的部分

type Config struct {Database struct {Host     string `yaml:"host"`Port     int    `yaml:"port"`User     string `yaml:"user"`Password string `yaml:"password"`DBName   string `yaml:"dbname"`SSLMode  string `yaml:"sslmode"`Timezone string `yaml:"timezone"`} `yaml:"database"`Server struct {HTTPPort int `yaml:"http_port"`GRPCPort int `yaml:"grpc_port"`} `yaml:"server"`
}

执行go run cmd/main.go的时候,发现服务的port是0
在这里插入图片描述
✅ viper 成功读取了 config.yaml 中server.http_portserver.grpc_port
❌ 但 viper.Unmarshal(&cfg) 没有把值映射到结构体中的 Server.HTTPPortServer.GRPCPort你正在使用 viper 的默认解码器(mapstructure),它不支持 yaml tag,只支持 mapstructure tag。也就是说:

HTTPPort int `yaml:"http_port"`

会被 YAML 正确解析,但 不会被 viper.Unmarshal 识别,因为 viper 默认使用 mapstructure 标签。

http://www.dtcms.com/wzjs/99185.html

相关文章:

  • 定制型网站建设平台网络软文怎么写
  • 网站品牌形象设计怎么做广告代运营公司
  • 重庆免费推广网站广州优化疫情防控措施
  • 用discuz做的大网站淘宝直通车
  • 怎么做网站生意网络推广营销
  • 无锡地区网站制作公司排名广州搜索seo网站优化
  • 建设返利优惠券网站百度售后客服电话24小时
  • 网站推广设计做哪些活动营销的方式有哪些
  • 网站目标建设网站内部优化有哪些内容
  • 网站之间的差异2024年新闻时事热点论文
  • 售后服务方案 网站建设网站搜索优化公司
  • 网站模板免费下载网站如何给自己的公司建网站
  • 东莞网站建设服务协议长沙网站seo优化排名
  • js做网站产品网络推广的方法有哪些
  • 厦门做网站最好的公司网站推广多少钱一年
  • 网站后台制作表格郑州seo排名优化
  • 手机把网站做成软件有哪些百度营销登录平台
  • 分析网站设计搜索引擎优化的缺点包括
  • 老网站绑定新网站如何做?百度贴吧广告投放价格
  • 只做男士衬衫的网站百度seo站长工具
  • 手机杭州网优化seo设置
  • 房产网站建设方案的论文网站一级域名和二级域名
  • 企业微信一年的费用要多少谷歌seo
  • discuz网站编码武汉seo顾问
  • web前端工资待遇百度优化服务
  • 上海网站建设推荐秒搜科技百度一下搜索引擎大全
  • 北京网站制作如何建立电商平台
  • 做零食网站的原因如何建网站不花钱
  • bootstrap做的导视网站seo网站推广优化论文
  • 上海 网站备案代理seo网站有哪些