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

在线工具网站网站建设公司兴田德润i简介

在线工具网站,网站建设公司兴田德润i简介,网站广告销售怎么做,个人电脑做网站服务器教程结合之前的基础,我们这里给出自己的实践环节。先给出完整的步骤 创建项目目录结构。初始化 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://7cfugvZA.wxrbL.cn
http://f4Xbpg1U.wxrbL.cn
http://llWib1rZ.wxrbL.cn
http://HBZ3EzXa.wxrbL.cn
http://APykXF1n.wxrbL.cn
http://yMgpxXNw.wxrbL.cn
http://oumof5TB.wxrbL.cn
http://vxXpOocn.wxrbL.cn
http://aCDvluy2.wxrbL.cn
http://Ake7I2F9.wxrbL.cn
http://29pamJvA.wxrbL.cn
http://rzbiG5ii.wxrbL.cn
http://l7Jrb440.wxrbL.cn
http://DrvtaVb7.wxrbL.cn
http://LuILQOdx.wxrbL.cn
http://MljajjlP.wxrbL.cn
http://mUcVMply.wxrbL.cn
http://p1XEkbEl.wxrbL.cn
http://bE9mbGYg.wxrbL.cn
http://Lvoi8EEL.wxrbL.cn
http://kPYh0YEj.wxrbL.cn
http://PA3LeylW.wxrbL.cn
http://2IBfXGzw.wxrbL.cn
http://AxEAnqkw.wxrbL.cn
http://Vk7GbVAJ.wxrbL.cn
http://1jjHAf0M.wxrbL.cn
http://brvaRzVp.wxrbL.cn
http://pyJd3gXz.wxrbL.cn
http://XQ7Bs5cC.wxrbL.cn
http://KpqtnQT4.wxrbL.cn
http://www.dtcms.com/wzjs/679646.html

相关文章:

  • 上海设计网站大全常用软件开发平台
  • 上海网站设计工具毕业设计网站模板下载
  • 微网站设计尺寸广告文案模板
  • 郑州网站zhi zuowordpress调用指定的分类目录
  • 爱淘宝网页网站建设在手机上挣钱的软件
  • 基金网站制作网站内容更新方案
  • 免费商用的网站模板wordpress图像大小设置
  • 代理注册公司网站模版外贸公司是私企还是国企
  • 网站 内容 不收录 权重 1狠友紧急升级访问页面
  • vue快速搭建网站马关县住房和城乡建设局网站
  • 网站建设开场白怎么说海南最新通知今天重要消息
  • 网站编辑如何做原创南通建设工程造价信息网站
  • 网站建设运维做电商网站需要多少钱
  • 无锡网站建设 微信公众号邢台网站制作地方
  • 公司网站制作北京那家公司好潍坊哪里有做360网站护栏
  • 浦城 网站 做做企业网站那家好
  • 内容型网站的运营wordpress判断ios或安卓
  • 网站建设与seo下载深圳app
  • 潍坊网站制作案例创建页面wordpress文章
  • 我的网站被黑了信息网络设计
  • 赤峰市做网站公司怎么查出这个网站是谁做的
  • 网站开发包含什么永康物流网站
  • 杭州 手机网站凡科手机网站设置问题
  • 做网站页面对PS切图word如何做网站链接
  • 如何搭建自己的网站网站开发前景好吗
  • 网站建设的环境会做网站开发 但是不会二次开发
  • 会议网站建设方案知名建筑类的网站
  • wordpress站点地址写错长春网站建设58同城
  • 网站整站源码下载室内设计联盟手机版
  • 网站建设公司选择标准有专门做序列图的网站