序言

我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下:

  1. 环境搭建
  2. 服务拆分
  3. 用户服务
  4. 产品服务
  5. 订单服务
  6. 支付服务(本文)
  7. RPC 服务 Auth 验证
  8. 服务监控
  9. 链路追踪
  10. 分布式事务

期望通过本系列带你在本机利用 Docker 环境利用 go-zero 快速开发一个商城系统,让你快速上手微服务。

完整示例代码:https://github.com/nivin-studio/go-zero-mall

首先,我们来看一下整体的服务拆分图:

6 支付服务(pay)

  • 进入服务工作区
$ cd mall/service/pay

6.1 生成 pay model 模型

  • 创建 sql 文件
$ vim model/pay.sql
  • 编写 sql 文件
CREATE TABLE `pay` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uid` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`oid` bigint unsigned NOT NULL DEFAULT '0' COMMENT '订单ID',
`amount` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '产品金额',
`source` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '支付方式',
`status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '支付状态',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_uid` (`uid`),
KEY `idx_oid` (`oid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  • 运行模板生成命令
$ goctl model mysql ddl -src ./model/pay.sql -dir ./model -c

6.2 生成 pay api 服务

  • 创建 api 文件
$ vim api/pay.api
  • 编写 api 文件
type (
// 支付创建
CreateRequest {
Uid int64 `json:"uid"`
Oid int64 `json:"oid"`
Amount int64 `json:"amount"`
}
CreateResponse {
Id int64 `json:"id"`
}
// 支付创建 // 支付详情
DetailRequest {
Id int64 `json:"id"`
}
DetailResponse {
Id int64 `json:"id"`
Uid int64 `json:"uid"`
Oid int64 `json:"oid"`
Amount int64 `json:"amount"`
Source int64 `json:"source"`
Status int64 `json:"status"`
}
// 支付详情 // 支付回调
CallbackRequest {
Id int64 `json:"id"`
Uid int64 `json:"uid"`
Oid int64 `json:"oid"`
Amount int64 `json:"amount"`
Source int64 `json:"source"`
Status int64 `json:"status"`
}
CallbackResponse {
}
// 支付回调 ) @server(
jwt: Auth
)
service Pay {
@handler Create
post /api/pay/create(CreateRequest) returns (CreateResponse) @handler Detail
post /api/pay/detail(DetailRequest) returns (DetailResponse) @handler Callback
post /api/pay/callback(CallbackRequest) returns (CallbackResponse)
}
  • 运行模板生成命令
$ goctl api go -api ./api/pay.api -dir ./api

6.3 生成 pay rpc 服务

  • 创建 proto 文件
$ vim rpc/pay.proto
  • 编写 proto 文件
syntax = "proto3";

package payclient;

option go_package = "pay";

// 支付创建
message CreateRequest {
int64 Uid = 1;
int64 Oid = 2;
int64 Amount = 3;
}
message CreateResponse {
int64 id = 1;
}
// 支付创建 // 支付详情
message DetailRequest {
int64 id = 1;
}
message DetailResponse {
int64 id = 1;
int64 Uid = 2;
int64 Oid = 3;
int64 Amount = 4;
int64 Source = 5;
int64 Status = 6;
}
// 支付详情 // 支付详情
message CallbackRequest {
int64 id = 1;
int64 Uid = 2;
int64 Oid = 3;
int64 Amount = 4;
int64 Source = 5;
int64 Status = 6;
}
message CallbackResponse {
}
// 支付详情 service Pay {
rpc Create(CreateRequest) returns(CreateResponse);
rpc Detail(DetailRequest) returns(DetailResponse);
rpc Callback(CallbackRequest) returns(CallbackResponse);
}
  • 运行模板生成命令
$ goctl rpc proto -src ./rpc/pay.proto -dir ./rpc

6.4 编写 pay rpc 服务

6.4.1 修改配置文件

  • 修改 pay.yaml 配置文件
$ vim rpc/etc/pay.yaml
  • 修改服务监听地址,端口号为0.0.0.0:9003,Etcd 服务配置,Mysql 服务配置,CacheRedis 服务配置
Name: pay.rpc
ListenOn: 0.0.0.0:9003 Etcd:
Hosts:
- etcd:2379
Key: pay.rpc Mysql:
DataSource: root:123456@tcp(mysql:3306)/mall?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis:
- Host: redis:6379
Type: node
Pass:

6.4.2 添加 pay model 依赖

  • 添加 Mysql 服务配置,CacheRedis 服务配置的实例化
$ vim rpc/internal/config/config.go
package config

import (
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/zrpc"
) type Config struct {
zrpc.RpcServerConf Mysql struct {
DataSource string
} CacheRedis cache.CacheConf
}
  • 注册服务上下文 pay model 的依赖
$ vim rpc/internal/svc/servicecontext.go
package svc

import (
"mall/service/pay/model"
"mall/service/pay/rpc/internal/config" "github.com/tal-tech/go-zero/core/stores/sqlx"
) type ServiceContext struct {
Config config.Config PayModel model.PayModel
} func NewServiceContext(c config.Config) *ServiceContext {
conn := sqlx.NewMysql(c.Mysql.DataSource)
return &ServiceContext{
Config: c,
PayModel: model.NewPayModel(conn, c.CacheRedis),
}
}

6.4.3 添加 user rpc,order rpc 依赖

  • 添加 user rpc, order rpc 服务配置
$ vim rpc/etc/pay.yaml
Name: pay.rpc
ListenOn: 0.0.0.0:9003
Etcd:
Hosts:
- etcd:2379
Key: pay.rpc ... UserRpc:
Etcd:
Hosts:
- etcd:2379
Key: user.rpc OrderRpc:
Etcd:
Hosts:
- etcd:2379
Key: order.rpc
  • 添加 user rpc, order rpc 服务配置的实例化
$ vim rpc/internal/config/config.go
package config

import (
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/zrpc"
) type Config struct {
zrpc.RpcServerConf Mysql struct {
DataSource string
} CacheRedis cache.CacheConf UserRpc zrpc.RpcClientConf
OrderRpc zrpc.RpcClientConf
}
  • 注册服务上下文 user rpc, order rpc 的依赖
$ vim rpc/internal/svc/servicecontext.go
package svc

import (
"mall/service/order/rpc/orderclient"
"mall/service/pay/model"
"mall/service/pay/rpc/internal/config"
"mall/service/user/rpc/userclient" "github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/zrpc"
) type ServiceContext struct {
Config config.Config PayModel model.PayModel UserRpc userclient.User
OrderRpc orderclient.Order
} func NewServiceContext(c config.Config) *ServiceContext {
conn := sqlx.NewMysql(c.Mysql.DataSource)
return &ServiceContext{
Config: c,
PayModel: model.NewPayModel(conn, c.CacheRedis),
UserRpc: userclient.NewUser(zrpc.MustNewClient(c.UserRpc)),
OrderRpc: orderclient.NewOrder(zrpc.MustNewClient(c.OrderRpc)),
}
}

6.4.4 添加支付创建逻辑 Create

  • 添加根据 oid 查询订单支付记录 PayModel 方法 FindOneByOid
$ vim model/paymodel.go
package model

...

var (
... cachePayIdPrefix = "cache:pay:id:"
cachePayOidPrefix = "cache:pay:oid:"
) type (
PayModel interface {
Insert(data *Pay) (sql.Result, error)
FindOne(id int64) (*Pay, error)
FindOneByOid(oid int64) (*Pay, error)
Update(data *Pay) error
Delete(id int64) error
} ...
) ... func (m *defaultPayModel) FindOneByOid(oid int64) (*Pay, error) {
payOidKey := fmt.Sprintf("%s%v", cachePayOidPrefix, oid)
var resp Pay
err := m.QueryRow(&resp, payOidKey, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `oid` = ? limit 1", payRows, m.table)
return conn.QueryRow(v, query, oid)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
} ......
  • 添加支付创建逻辑

    支付流水创建流程,通过调用 user rpc 服务查询验证用户是否存在,再通过调用 order rpc 服务查询验证订单是否存在,然后通过查询库判断此订单是否已经创建过支付流水,最后创建落库。

$ vim rpc/internal/logic/createlogic.go
package logic

import (
"context" "mall/service/order/rpc/order"
"mall/service/pay/model"
"mall/service/pay/rpc/internal/svc"
"mall/service/pay/rpc/pay"
"mall/service/user/rpc/user" "github.com/tal-tech/go-zero/core/logx"
"google.golang.org/grpc/status"
) 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 *pay.CreateRequest) (*pay.CreateResponse, error) {
// 查询用户是否存在
_, err := l.svcCtx.UserRpc.UserInfo(l.ctx, &user.UserInfoRequest{
Id: in.Uid,
})
if err != nil {
return nil, err
} // 查询订单是否存在
_, err = l.svcCtx.OrderRpc.Detail(l.ctx, &order.DetailRequest{
Id: in.Oid,
})
if err != nil {
return nil, err
} // 查询订单是否已经创建支付
_, err = l.svcCtx.PayModel.FindOneByOid(in.Oid)
if err == nil {
return nil, status.Error(100, "订单已创建支付")
} newPay := model.Pay{
Uid: in.Uid,
Oid: in.Oid,
Amount: in.Amount,
Source: 0,
Status: 0,
} res, err := l.svcCtx.PayModel.Insert(&newPay)
if err != nil {
return nil, status.Error(500, err.Error())
} newPay.Id, err = res.LastInsertId()
if err != nil {
return nil, status.Error(500, err.Error())
} return &pay.CreateResponse{
Id: newPay.Id,
}, nil
}

6.4.5 添加支付详情逻辑 Detail

$ vim rpc/internal/logic/detaillogic.go
package logic

import (
"context" "mall/service/pay/model"
"mall/service/pay/rpc/internal/svc"
"mall/service/pay/rpc/pay" "github.com/tal-tech/go-zero/core/logx"
"google.golang.org/grpc/status"
) type DetailLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
} func NewDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DetailLogic {
return &DetailLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
} func (l *DetailLogic) Detail(in *pay.DetailRequest) (*pay.DetailResponse, error) {
// 查询支付是否存在
res, err := l.svcCtx.PayModel.FindOne(in.Id)
if err != nil {
if err == model.ErrNotFound {
return nil, status.Error(100, "支付不存在")
}
return nil, status.Error(500, err.Error())
} return &pay.DetailResponse{
Id: res.Id,
Uid: res.Uid,
Oid: res.Oid,
Amount: res.Amount,
Source: res.Source,
Status: res.Status,
}, nil
}

6.4.6 添加支付回调逻辑 Callback

支付流水回调流程,通过调用 user rpc 服务查询验证用户是否存在,再通过调用 order rpc 服务查询验证订单是否存在,然后通过查询库判断此订单支付流水是否存在,以及回调支付金额和库中流水支付金额是否一致,最后更新支付流水状态和通过调用 order rpc 服务更新订单状态。

$ vim rpc/internal/logic/callbacklogic.go
package logic

import (
"context" "mall/service/order/rpc/order"
"mall/service/pay/model"
"mall/service/pay/rpc/internal/svc"
"mall/service/pay/rpc/pay"
"mall/service/user/rpc/user" "github.com/tal-tech/go-zero/core/logx"
"google.golang.org/grpc/status"
) type CallbackLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
} func NewCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CallbackLogic {
return &CallbackLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
} func (l *CallbackLogic) Callback(in *pay.CallbackRequest) (*pay.CallbackResponse, error) {
// 查询用户是否存在
_, err := l.svcCtx.UserRpc.UserInfo(l.ctx, &user.UserInfoRequest{
Id: in.Uid,
})
if err != nil {
return nil, err
} // 查询订单是否存在
_, err = l.svcCtx.OrderRpc.Detail(l.ctx, &order.DetailRequest{
Id: in.Oid,
})
if err != nil {
return nil, err
} // 查询支付是否存在
res, err := l.svcCtx.PayModel.FindOne(in.Id)
if err != nil {
if err == model.ErrNotFound {
return nil, status.Error(100, "支付不存在")
}
return nil, status.Error(500, err.Error())
}
// 支付金额与订单金额不符
if in.Amount != res.Amount {
return nil, status.Error(100, "支付金额与订单金额不符")
} res.Source = in.Source
res.Status = in.Status err = l.svcCtx.PayModel.Update(res)
if err != nil {
return nil, status.Error(500, err.Error())
} // 更新订单支付状态
_, err = l.svcCtx.OrderRpc.Paid(l.ctx, &order.PaidRequest{
Id: in.Oid,
})
if err != nil {
return nil, status.Error(500, err.Error())
} return &pay.CallbackResponse{}, nil
}

6.5 编写 pay api 服务

6.5.1 修改配置文件

  • 修改 pay.yaml 配置文件
$ vim api/etc/pay.yaml
  • 修改服务地址,端口号为0.0.0.0:8003,Mysql 服务配置,CacheRedis 服务配置,Auth 验证配置
Name: Pay
Host: 0.0.0.0
Port: 8003 Mysql:
DataSource: root:123456@tcp(mysql:3306)/mall?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis:
- Host: redis:6379
Type: node
Pass: Auth:
AccessSecret: uOvKLmVfztaXGpNYd4Z0I1SiT7MweJhl
AccessExpire: 86400

6.5.2 添加 pay rpc 依赖

  • 添加 pay rpc 服务配置
$ vim api/etc/pay.yaml
Name: Pay
Host: 0.0.0.0
Port: 8003 ...... PayRpc:
Etcd:
Hosts:
- etcd:2379
Key: pay.rpc
  • 添加 pay rpc 服务配置的实例化
$ vim api/internal/config/config.go
package config

import (
"github.com/tal-tech/go-zero/rest"
"github.com/tal-tech/go-zero/zrpc"
) type Config struct {
rest.RestConf Auth struct {
AccessSecret string
AccessExpire int64
} PayRpc zrpc.RpcClientConf
}
  • 注册服务上下文 pay rpc 的依赖
$ vim api/internal/svc/servicecontext.go
package svc

import (
"mall/service/pay/api/internal/config"
"mall/service/pay/rpc/payclient" "github.com/tal-tech/go-zero/zrpc"
) type ServiceContext struct {
Config config.Config PayRpc payclient.Pay
} func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
PayRpc: payclient.NewPay(zrpc.MustNewClient(c.PayRpc)),
}
}

6.5.3 添加支付创建逻辑 Create

$ vim api/internal/logic/createlogic.go
package logic

import (
"context" "mall/service/pay/api/internal/svc"
"mall/service/pay/api/internal/types"
"mall/service/pay/rpc/pay" "github.com/tal-tech/go-zero/core/logx"
) type CreateLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
} func NewCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) CreateLogic {
return CreateLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
} func (l *CreateLogic) Create(req types.CreateRequest) (resp *types.CreateResponse, err error) {
res, err := l.svcCtx.PayRpc.Create(l.ctx, &pay.CreateRequest{
Uid: req.Uid,
Oid: req.Oid,
Amount: req.Amount,
})
if err != nil {
return nil, err
} return &types.CreateResponse{
Id: res.Id,
}, nil
}

6.5.4 添加支付详情逻辑 Detail

$ vim api/internal/logic/detaillogic.go
package logic

import (
"context" "mall/service/pay/api/internal/svc"
"mall/service/pay/api/internal/types"
"mall/service/pay/rpc/pay" "github.com/tal-tech/go-zero/core/logx"
) type DetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
} func NewDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) DetailLogic {
return DetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
} func (l *DetailLogic) Detail(req types.DetailRequest) (resp *types.DetailResponse, err error) {
res, err := l.svcCtx.PayRpc.Detail(l.ctx, &pay.DetailRequest{
Id: req.Id,
})
if err != nil {
return nil, err
} return &types.DetailResponse{
Id: req.Id,
Uid: res.Uid,
Oid: res.Oid,
Amount: res.Amount,
Source: res.Source,
Status: res.Status,
}, nil
}

6.5.5 添加支付回调逻辑 Callback

$ vim api/internal/logic/callbacklogic.go
package logic

import (
"context" "mall/service/pay/api/internal/svc"
"mall/service/pay/api/internal/types"
"mall/service/pay/rpc/pay" "github.com/tal-tech/go-zero/core/logx"
) type CallbackLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
} func NewCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) CallbackLogic {
return CallbackLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
} func (l *CallbackLogic) Callback(req types.CallbackRequest) (resp *types.CallbackResponse, err error) {
_, err = l.svcCtx.PayRpc.Callback(l.ctx, &pay.CallbackRequest{
Id: req.Id,
Uid: req.Uid,
Oid: req.Oid,
Amount: req.Amount,
Source: req.Source,
Status: req.Status,
})
if err != nil {
return nil, err
} return &types.CallbackResponse{}, nil
}

6.6 启动 pay rpc 服务

提示:启动服务需要在 golang 容器中启动

$ cd mall/service/pay/rpc
$ go run pay.go -f etc/pay.yaml
Starting rpc server at 127.0.0.1:9003...

6.7 启动 pay api 服务

提示:启动服务需要在 golang 容器中启动

$ cd mall/service/pay/api
$ go run pay.go -f etc/pay.yaml
Starting server at 0.0.0.0:8003...

项目地址

https://github.com/zeromicro/go-zero

欢迎使用 go-zerostar 支持我们!

微信交流群

关注『微服务实践』公众号并点击 交流群 获取社区群二维码。

带你十天轻松搞定 Go 微服务系列(六)的更多相关文章

  1. 带你十天轻松搞定 Go 微服务系列(一)

    本文开始,我们会出一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建(本文) 服务拆分 用户服务 产品服务 订单服务 支付服务 RPC 服务 Au ...

  2. 带你十天轻松搞定 Go 微服务系列(二)

    上篇文章开始,我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分(本文) 用户服务 产品服务 订单服务 支付服务 RPC 服务 ...

  3. 带你十天轻松搞定 Go 微服务系列(三)

    序言 我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分 用户服务(本文) 产品服务 订单服务 支付服务 RPC 服务 Auth ...

  4. 带你十天轻松搞定 Go 微服务系列(五)

    序言 我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分 用户服务 产品服务 订单服务(本文) 支付服务 RPC 服务 Auth ...

  5. 带你十天轻松搞定 Go 微服务系列(七)

    序言 我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分 用户服务 产品服务 订单服务 支付服务 RPC 服务 Auth 验证( ...

  6. 带你十天轻松搞定 Go 微服务系列(八、服务监控)

    序言 我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分 用户服务 产品服务 订单服务 支付服务 RPC 服务 Auth 验证 ...

  7. 带你十天轻松搞定 Go 微服务系列(九、链路追踪)

    序言 我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分 用户服务 产品服务 订单服务 支付服务 RPC 服务 Auth 验证 ...

  8. 带你十天轻松搞定 Go 微服务之大结局(分布式事务)

    序言 我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分 用户服务 产品服务 订单服务 支付服务 RPC 服务 Auth 验证 ...

  9. 【微服务】之二:从零开始,轻松搞定SpringCloud微服务系列--注册中心(一)

    微服务体系,有效解决项目庞大.互相依赖的问题.目前SpringCloud体系有强大的一整套针对微服务的解决方案.本文中,重点对微服务体系中的服务发现注册中心进行详细说明.本篇中的注册中心,采用Netf ...

随机推荐

  1. 1374 - Confusion in the Problemset

    1374 - Confusion in the Problemset    PDF (English) Statistics Forum Time Limit: 2 second(s) Memory ...

  2. 响应式网页设计(Bootstrap)

    Bootstrap官网 AOS官网 Chrome官方教程 Chrome教程 Bootstrap官网中有许多Bootstrap网站示例,大家可以参考

  3. Kronecker Products and Stack Operator

    目录 定义 Stack Operator Kronecker Product 性质 Stack Operator Kronecker Product 半线性 Whitcomb L. Notes on ...

  4. ADADELTA: AN ADAPTIVE LEARNING RATE METHOD

    目录 引 主要内容 算法 ADADELTA 代码 引 这篇论文比较短,先看了这篇,本来应该先把ADAGRAD看了的.普通的基于梯度下降的方法,普遍依赖于步长,起始点的选择,所以,受ADAGRAD的启发 ...

  5. [opencv]拟合vector<Mat>集合区域接近的元素

    vector<Rect> PublicCardFrameDetection::fitrect(vector<Rect> rects){ int size = rects.siz ...

  6. BL8810|USB2.0高速闪存读卡器芯片|BL8810规格书

    1.说明 BL8810是一款USB 2.0读卡器控制器,采用高度集成的单芯片解决方案,旨在提供USB2.0和SD.SDHC.mini SD.Micro SD(T-Flash)接口规范之间的高速数据传输 ...

  7. 【SpringBoot】No 'Access-Control-Allow-Origin' header is present on the requested resource.

    关键字:跨域,Access-Control-Allow-Origin,转码,解码 在做一个前后端分离项目,本来前端项目都可以正常访问后端接口,跨域是这么设置的,接口可以正常访问 @Configurat ...

  8. Eclipse导入Zookeeper源码Version2017.11.3

    将Zookeeper源码导入Eclipse, Zookeeper源码需要使用ant构建后才能导入Eclipse, 和Solr的源码一样也是使用ant构建的, 大部分可以参考Eclipse导入Solr源 ...

  9. OAuth2.0的定义

    1. 什么是OAuth2.0 * 用于REST/APIs的代理授权框架(delegated authorization) * 基于令牌Token的授权,在无需暴露用户密码的情况下,使应用能获取对用户数 ...

  10. Lomsat gelral

    题目描述 You are given a rooted tree with root in vertex 11 . Each vertex is coloured in some colour. Le ...