订单回调
This commit is contained in:
parent
ee7a93f73d
commit
046418534a
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
# 使用官方Go镜像作为构建环境
|
||||||
|
FROM golang:1.21.0 AS builder
|
||||||
|
|
||||||
|
# 设置环境变量的默认值
|
||||||
|
ENV SERVER=api
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 设置环境变量
|
||||||
|
ENV GOPROXY=https://goproxy.cn,direct
|
||||||
|
|
||||||
|
# 复制项目源码
|
||||||
|
COPY . .
|
||||||
|
# 安装snow-core
|
||||||
|
RUN go get github.com/qit-team/snow-core/kernel/server@v0.1.28
|
||||||
|
# 安装go模块依赖
|
||||||
|
RUN go mod tidy
|
||||||
|
|
||||||
|
# 编译Go应用程序,生成静态链接的二进制文件
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server main.go
|
||||||
|
|
||||||
|
# 创建最终镜像,用于运行编译后的Go程序
|
||||||
|
FROM alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
# 将编译好的二进制文件从构建阶段复制到运行阶段
|
||||||
|
COPY --from=builder /app/server /app/server
|
||||||
|
COPY --from=builder /app/.env /app/.env
|
||||||
|
RUN chmod +x /app/server
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 暴露容器的端口
|
||||||
|
EXPOSE 443 8083 8082
|
||||||
|
|
||||||
|
# 设置容器启动时运行的命令
|
||||||
|
ENTRYPOINT ["/app/server", "-a", "api"]
|
|
@ -28,6 +28,8 @@ const (
|
||||||
OrderProductNotExist = 1002
|
OrderProductNotExist = 1002
|
||||||
//兴业下单失败
|
//兴业下单失败
|
||||||
XINYEOrderFAIL = 1003
|
XINYEOrderFAIL = 1003
|
||||||
|
//商品库存不足
|
||||||
|
ProductStockFAIL = 1004
|
||||||
|
|
||||||
//订单不允许退款
|
//订单不允许退款
|
||||||
OrderNOTAuthREFUND = 2001
|
OrderNOTAuthREFUND = 2001
|
||||||
|
@ -62,6 +64,7 @@ var MsgZH = map[int]string{
|
||||||
OrderRefundUpdateFail: "订单更新失败",
|
OrderRefundUpdateFail: "订单更新失败",
|
||||||
OrderRefundFail: "退款失败",
|
OrderRefundFail: "退款失败",
|
||||||
YouChuOrderRefundFail: "邮储服务异常",
|
YouChuOrderRefundFail: "邮储服务异常",
|
||||||
|
ProductStockFAIL: "库存不足",
|
||||||
}
|
}
|
||||||
var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH}
|
var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package front
|
||||||
import (
|
import (
|
||||||
"github.com/ahmetb/go-linq/v3"
|
"github.com/ahmetb/go-linq/v3"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
"qteam/app/constants/common"
|
"qteam/app/constants/common"
|
||||||
"qteam/app/constants/errorcode"
|
"qteam/app/constants/errorcode"
|
||||||
"qteam/app/http/controllers"
|
"qteam/app/http/controllers"
|
||||||
|
@ -28,9 +29,9 @@ func OrderList(c *gin.Context) {
|
||||||
//userId := controllers.GetUserId(c)
|
//userId := controllers.GetUserId(c)
|
||||||
userId := 1
|
userId := 1
|
||||||
code, orderList, count := services.OrderQueryService(userId, request)
|
code, orderList, count := services.OrderQueryService(userId, request)
|
||||||
var rsp []front.OrderQueryResponse
|
var rsp []front.OrderListResponse
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
linq.From(orderList).SelectT(func(in ordersmodel.Orders) (out front.OrderQueryResponse) {
|
linq.From(orderList).SelectT(func(in ordersmodel.OrdersProductsList) (out front.OrderListResponse) {
|
||||||
out.ResponseFromDb(in)
|
out.ResponseFromDb(in)
|
||||||
return out
|
return out
|
||||||
}).ToSlice(&rsp)
|
}).ToSlice(&rsp)
|
||||||
|
@ -41,7 +42,8 @@ func OrderList(c *gin.Context) {
|
||||||
func OrderQuery(c *gin.Context) {
|
func OrderQuery(c *gin.Context) {
|
||||||
var request = controllers.GetRequest(c).(*front.OrderQueryRequest)
|
var request = controllers.GetRequest(c).(*front.OrderQueryRequest)
|
||||||
orderId, _ := strconv.Atoi(request.OrderId)
|
orderId, _ := strconv.Atoi(request.OrderId)
|
||||||
order := ordersmodel.Orders{Id: orderId}
|
order := ordersmodel.Orders{}
|
||||||
|
order.Id = orderId
|
||||||
var OrderQueryResponse front.OrderQueryResponse
|
var OrderQueryResponse front.OrderQueryResponse
|
||||||
has, err := services.OrderDetailService(&order)
|
has, err := services.OrderDetailService(&order)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -79,13 +81,15 @@ func OrderQuery(c *gin.Context) {
|
||||||
|
|
||||||
func OrderRefund(c *gin.Context) {
|
func OrderRefund(c *gin.Context) {
|
||||||
var request = controllers.GetRequest(c).(*front.OrderQueryRequest)
|
var request = controllers.GetRequest(c).(*front.OrderQueryRequest)
|
||||||
//userId := controllers.GetUserId(c)
|
userId := controllers.GetUserId(c)
|
||||||
userId := 1
|
//userId := 1
|
||||||
OrderId, _ := strconv.Atoi(request.OrderId)
|
OrderId, _ := strconv.Atoi(request.OrderId)
|
||||||
code := services.OrderRefundService(userId, OrderId)
|
code := services.OrderRefundService(userId, OrderId)
|
||||||
controllers.HandCodeRes(c, nil, code)
|
controllers.HandCodeRes(c, nil, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrderNotify(c *gin.Context) {
|
func OrderNotify(c *gin.Context) {
|
||||||
|
var request = controllers.GetRequest(c).(*front.YouChuRequest)
|
||||||
|
response := services.OrderNotify(*request)
|
||||||
|
c.JSON(http.StatusOK, response)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package front
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"qteam/app/http/controllers"
|
||||||
|
"qteam/app/http/entities/front"
|
||||||
|
"qteam/app/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProductDetail(c *gin.Context) {
|
||||||
|
var request = controllers.GetRequest(c).(*front.ProductDetailRequest)
|
||||||
|
code, product := services.ProductDetail(*request)
|
||||||
|
var response = front.ProductDetailResponse{}
|
||||||
|
response.ResponseFromDb(product)
|
||||||
|
controllers.HandCodeRes(c, response, code)
|
||||||
|
}
|
|
@ -15,6 +15,36 @@ type OrderQueryRequest struct {
|
||||||
|
|
||||||
type OrderListRequest struct {
|
type OrderListRequest struct {
|
||||||
PageRequest
|
PageRequest
|
||||||
|
State int `form:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderListResponse struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
OrderNo string `json:"order_no"`
|
||||||
|
MainImage string `json:"main_image"`
|
||||||
|
UserId int `json:"user_id"`
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
ProductId int `json:"product_id"`
|
||||||
|
ProductName string `json:"product_name"`
|
||||||
|
State int `json:"state"`
|
||||||
|
VoucherLink string `json:"voucher_link"`
|
||||||
|
CreateTime string `json:"create_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderListResponse) ResponseFromDb(l ordersmodel.OrdersProductsList) {
|
||||||
|
p.Id = l.Id
|
||||||
|
p.OrderNo = l.OrderNo
|
||||||
|
p.MainImage = l.MainImage
|
||||||
|
p.UserId = l.UserId
|
||||||
|
p.UserName = l.UserName
|
||||||
|
p.Mobile = l.Mobile
|
||||||
|
p.ProductId = l.ProductId
|
||||||
|
p.ProductName = l.ProductName
|
||||||
|
p.State = l.State
|
||||||
|
p.VoucherLink = l.VoucherLink
|
||||||
|
p.CreateTime = l.CreateTime.Format("2006-01-02 15:04:05")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderQueryResponse struct {
|
type OrderQueryResponse struct {
|
||||||
|
|
|
@ -1 +1,28 @@
|
||||||
package front
|
package front
|
||||||
|
|
||||||
|
import (
|
||||||
|
"qteam/app/models/productsmodel"
|
||||||
|
"qteam/app/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProductDetailRequest struct {
|
||||||
|
ProductId int `form:"product_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductDetailResponse struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
MainImage string `json:"main_image"`
|
||||||
|
Brand string `json:"brand"`
|
||||||
|
Stock string `json:"stock"`
|
||||||
|
ShowPrice string `json:"show_price"`
|
||||||
|
Price string `json:"price"`
|
||||||
|
CreateTime string `json:"create_time"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ProductDetailResponse) ResponseFromDb(in productsmodel.Products) {
|
||||||
|
utils.EntityCopy(this, &in)
|
||||||
|
this.CreateTime = in.CreateTime.Format("2006-01-02 15:04:05")
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"qteam/app/models/brandmodel"
|
"qteam/app/models/brandmodel"
|
||||||
"qteam/app/models/productsmodel"
|
"qteam/app/models/productsmodel"
|
||||||
"qteam/config"
|
"qteam/config"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type VoucherListResponse struct {
|
type VoucherListResponse struct {
|
||||||
|
@ -23,6 +24,7 @@ func (this *MilkList) ResponseFromDb(in brandmodel.Brand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MilkVoucherList struct {
|
type MilkVoucherList struct {
|
||||||
|
ProductId string `json:"ProductId"`
|
||||||
BrandFlag string `json:"brandFlag"`
|
BrandFlag string `json:"brandFlag"`
|
||||||
VoucherTitle string `json:"voucherTitle"`
|
VoucherTitle string `json:"voucherTitle"`
|
||||||
VoucherUrl string `json:"voucherUrl"`
|
VoucherUrl string `json:"voucherUrl"`
|
||||||
|
@ -33,11 +35,17 @@ type MilkVoucherList struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *MilkVoucherList) ResponseFromDb(in productsmodel.MilkProductsList) {
|
func (this *MilkVoucherList) ResponseFromDb(in productsmodel.MilkProductsList) {
|
||||||
|
this.ProductId = in.Id
|
||||||
this.BrandFlag = in.Flag
|
this.BrandFlag = in.Flag
|
||||||
this.VoucherTitle = in.Name
|
this.VoucherTitle = in.Name
|
||||||
this.VoucherUrl = config.GetConf().YouChu.MilkUrl + "?voucher_id=" + in.Id
|
this.VoucherUrl = config.GetConf().YouChu.VoucherUrl + "?product_id=" + in.Id
|
||||||
this.VoucherIcon = in.MainImage
|
this.VoucherIcon = in.MainImage
|
||||||
this.VoucherAmount = in.ShowPrice
|
this.VoucherAmount = in.ShowPrice
|
||||||
this.VoucherOriginalPrice = in.Price
|
this.VoucherOriginalPrice = in.Price
|
||||||
this.VoucherStatus = in.Status
|
stock, _ := strconv.Atoi(in.Stock)
|
||||||
|
if stock > 0 {
|
||||||
|
this.VoucherStatus = "1"
|
||||||
|
} else {
|
||||||
|
this.VoucherStatus = "0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,26 @@ type YouChuRequest struct {
|
||||||
EncryptKey string `json:"encryptKey"`
|
EncryptKey string `json:"encryptKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type YouChuLoginRequest struct {
|
||||||
|
Body YouChuLoginBody `json:"body"`
|
||||||
|
Head YouChuRequestHeader `json:"head"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type YouChuLoginResponse struct {
|
||||||
|
RespCode string `json:"respCode"` //000000-成功 069801-系统繁忙,请稍后重试 820007-未查询到客户号
|
||||||
|
RespMsg string `json:"respMsg"` //描 述 (失败时必填)
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
IdtLastFour string `json:"idtLastFour"`
|
||||||
|
CustName string `json:"custName"`
|
||||||
|
CustFlag string `json:"custFlag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type YouChuLoginBody struct {
|
||||||
|
CustNo string `json:"custNo"`
|
||||||
|
ReqTransTime string `json:"reqTransTime"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
type YouChuRequestBody struct {
|
type YouChuRequestBody struct {
|
||||||
Body YouChuOrderRequest `json:"body"`
|
Body YouChuOrderRequest `json:"body"`
|
||||||
Head YouChuRequestHeader `json:"head"`
|
Head YouChuRequestHeader `json:"head"`
|
||||||
|
@ -92,6 +112,22 @@ type RefundRequestData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type YouChuOrderNotifyRequest struct {
|
type YouChuOrderNotifyRequest struct {
|
||||||
|
TxnCode string `json:"txnCode"`
|
||||||
|
SourceId string `json:"sourceId"`
|
||||||
|
ReqDate string `json:"reqDate"`
|
||||||
|
ReqTraceId string `json:"reqTraceId"`
|
||||||
|
OrderNo string `json:"orderNo"`
|
||||||
|
TxnAmt string `json:"txnAmt"`
|
||||||
|
OrderSta string `json:"orderSta"` //03-支付成功 04-支付失败05-检查失败
|
||||||
|
MchtNo string `json:"mchtNo"`
|
||||||
|
TermId string `json:"termId"`
|
||||||
|
TxnFlag string `json:"txnFlag"` //01-消费 03-退货
|
||||||
|
TxnKind string `json:"txnKind"` //01-银行卡 02-微信 03-支付宝
|
||||||
|
}
|
||||||
|
|
||||||
|
type YouChuOrderNotifyResponse struct {
|
||||||
|
RespCode string `json:"respCode"` //000000 表示通知成功
|
||||||
|
RespMsg string `json:"respMsg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *YouChuOrderRequest) ToMap() (resultMap map[string]interface{}) {
|
func (this *YouChuOrderRequest) ToMap() (resultMap map[string]interface{}) {
|
||||||
|
|
|
@ -10,7 +10,10 @@ var FrontRequestMap = map[string]func() interface{}{
|
||||||
common.FRONT_API_V1 + "/VoucherList": func() interface{} { return new(struct{}) },
|
common.FRONT_API_V1 + "/VoucherList": func() interface{} { return new(struct{}) },
|
||||||
//联合登录
|
//联合登录
|
||||||
common.FRONT_API_V1 + "/UnionLogin": func() interface{} { return new(front.UnionLoginRequest) },
|
common.FRONT_API_V1 + "/UnionLogin": func() interface{} { return new(front.UnionLoginRequest) },
|
||||||
|
//商品详情
|
||||||
|
common.FRONT_API_V1 + "/product/detail": func() interface{} { return new(front.ProductDetailRequest) },
|
||||||
|
//订单回调
|
||||||
|
common.FRONT_API_V1 + "/order/notify": func() interface{} { return new(front.YouChuRequest) },
|
||||||
// 生成订单
|
// 生成订单
|
||||||
common.FRONT_API_V1_Auth + "/order/create": func() interface{} { return new(front.OrderCreateRequest) },
|
common.FRONT_API_V1_Auth + "/order/create": func() interface{} { return new(front.OrderCreateRequest) },
|
||||||
//订单列表
|
//订单列表
|
||||||
|
|
|
@ -52,8 +52,10 @@ func RegisterRoute(router *gin.Engine) {
|
||||||
v1.POST("/VoucherList", front.VoucherList)
|
v1.POST("/VoucherList", front.VoucherList)
|
||||||
// 联合登录
|
// 联合登录
|
||||||
v1.POST("/UnionLogin", front.UnionLogin)
|
v1.POST("/UnionLogin", front.UnionLogin)
|
||||||
|
// 订单回调
|
||||||
v1.POST("/order/notify", front.OrderNotify)
|
v1.POST("/order/notify", front.OrderNotify)
|
||||||
|
//商品详情
|
||||||
|
v1.POST("/product/detail", front.ProductDetail)
|
||||||
|
|
||||||
//auth
|
//auth
|
||||||
auth := v1.Group("auth")
|
auth := v1.Group("auth")
|
||||||
|
|
|
@ -27,13 +27,18 @@ type Orders struct {
|
||||||
VoucherLink string `xorm:"'voucher_link' varchar(255)"`
|
VoucherLink string `xorm:"'voucher_link' varchar(255)"`
|
||||||
RefundOrderNo string `xorm:"'refundOrderNo' varchar(255)"`
|
RefundOrderNo string `xorm:"'refundOrderNo' varchar(255)"`
|
||||||
RefundOrderSta string `xorm:"'refundOrderSta' varchar(20)"`
|
RefundOrderSta string `xorm:"'refundOrderSta' varchar(20)"`
|
||||||
RefundTime time.Time `xorm:"'create_time' datetime"`
|
RefundTime time.Time `xorm:"'refund_time' datetime"`
|
||||||
CreateTime time.Time `xorm:"'create_time' datetime"`
|
CreateTime time.Time `xorm:"'create_time' datetime"`
|
||||||
UpdateTime time.Time `xorm:"'update_time' datetime"`
|
UpdateTime time.Time `xorm:"'update_time' datetime"`
|
||||||
ExchangeTime time.Time `xorm:"'exchange_time' datetime"`
|
ExchangeTime time.Time `xorm:"'exchange_time' datetime"`
|
||||||
Deleted time.Time `xorm:"'Deleted' datetime"`
|
Deleted time.Time `xorm:"'Deleted' datetime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrdersProductsList struct {
|
||||||
|
Orders `xorm:"extends"`
|
||||||
|
MainImage string `xorm:"'main_image' varchar(255)"`
|
||||||
|
}
|
||||||
|
|
||||||
// 表名
|
// 表名
|
||||||
func (m *Orders) TableName() string {
|
func (m *Orders) TableName() string {
|
||||||
return "Orders"
|
return "Orders"
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"qteam/app/constants/common"
|
"qteam/app/constants/common"
|
||||||
"qteam/app/constants/errorcode"
|
"qteam/app/constants/errorcode"
|
||||||
"qteam/app/http/entities/front"
|
"qteam/app/http/entities/front"
|
||||||
"qteam/app/models/ordersmodel"
|
"qteam/app/models/ordersmodel"
|
||||||
"qteam/app/models/productsmodel"
|
"qteam/app/models/productsmodel"
|
||||||
"qteam/app/models/usersmodel"
|
"qteam/app/models/usersmodel"
|
||||||
|
"qteam/app/third/market"
|
||||||
|
"qteam/app/third/youchu"
|
||||||
"qteam/app/utils"
|
"qteam/app/utils"
|
||||||
redis_util "qteam/app/utils/redis"
|
redis_util "qteam/app/utils/redis"
|
||||||
"qteam/config"
|
"qteam/config"
|
||||||
|
@ -46,6 +49,10 @@ func CreateOrderService(userId int, productId int) (code int, data front.InsertO
|
||||||
if !has {
|
if !has {
|
||||||
return errorcode.OrderProductNotExist, data
|
return errorcode.OrderProductNotExist, data
|
||||||
}
|
}
|
||||||
|
stock, _ := strconv.Atoi(product.Stock)
|
||||||
|
if stock <= 0 {
|
||||||
|
return errorcode.ProductStockFAIL, data
|
||||||
|
}
|
||||||
var user usersmodel.Users
|
var user usersmodel.Users
|
||||||
_, err = usersmodel.GetInstance().GetDb().Where("id =?", userId).Get(&user)
|
_, err = usersmodel.GetInstance().GetDb().Where("id =?", userId).Get(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,9 +84,15 @@ func CreateOrderService(userId int, productId int) (code int, data front.InsertO
|
||||||
return errorcode.Success, data
|
return errorcode.Success, data
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrderQueryService(userId int, OrderRequest *front.OrderListRequest) (code int, data []ordersmodel.Orders, count int64) {
|
func OrderQueryService(userId int, OrderRequest *front.OrderListRequest) (code int, data []ordersmodel.OrdersProductsList, count int64) {
|
||||||
repo := ordersmodel.GetInstance().GetDb()
|
repo := ordersmodel.GetInstance().GetDb()
|
||||||
count, err := repo.Where("user_id = ?", userId).
|
conn := builder.NewCond()
|
||||||
|
conn = conn.And(builder.Eq{"a.user_id": userId})
|
||||||
|
if OrderRequest.State != 0 {
|
||||||
|
conn = conn.And(builder.Eq{"a.state": OrderRequest.State})
|
||||||
|
}
|
||||||
|
count, err := repo.Where(conn).Alias("a").
|
||||||
|
Join("INNER", "Products b", "a.product_id = b.id").Select("a.*,b.main_image").
|
||||||
Desc("id").Limit(OrderRequest.PageSize, (OrderRequest.Page-1)*OrderRequest.PageSize).FindAndCount(&data)
|
Desc("id").Limit(OrderRequest.PageSize, (OrderRequest.Page-1)*OrderRequest.PageSize).FindAndCount(&data)
|
||||||
code = handErr(err)
|
code = handErr(err)
|
||||||
return
|
return
|
||||||
|
@ -126,7 +139,8 @@ func OrderRefundService(userId int, orderId int) (code int) {
|
||||||
return errorcode.SystemError
|
return errorcode.SystemError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
order := ordersmodel.Orders{Id: orderId, UserId: userId}
|
order := ordersmodel.Orders{}
|
||||||
|
order.Id = orderId
|
||||||
has, err := OrderDetailService(&order)
|
has, err := OrderDetailService(&order)
|
||||||
if err != nil || !has {
|
if err != nil || !has {
|
||||||
return errorcode.NotFound
|
return errorcode.NotFound
|
||||||
|
@ -153,3 +167,62 @@ func OrderRefundService(userId int, orderId int) (code int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OrderNotify(request front.YouChuRequest) (NotifyResponse front.YouChuOrderNotifyResponse) {
|
||||||
|
requestStr, _ := json.Marshal(request)
|
||||||
|
utils.Log(nil, "OrderNotify", string(requestStr))
|
||||||
|
response := youchu.DecryptResponse(string(requestStr))
|
||||||
|
var YouChuOrderNotifyRequest front.YouChuOrderNotifyRequest
|
||||||
|
err := json.Unmarshal([]byte(response), &YouChuOrderNotifyRequest)
|
||||||
|
if err != nil {
|
||||||
|
return front.YouChuOrderNotifyResponse{RespCode: "000001", RespMsg: "解析失败"}
|
||||||
|
}
|
||||||
|
utils.Log(nil, "YouChuOrderNotifyRequest", YouChuOrderNotifyRequest)
|
||||||
|
// 03-支付成功 04-支付失败 05-检查失败
|
||||||
|
if YouChuOrderNotifyRequest.OrderSta != "03" {
|
||||||
|
return front.YouChuOrderNotifyResponse{RespCode: "000002", RespMsg: "交易失败"}
|
||||||
|
} else {
|
||||||
|
//支付成功
|
||||||
|
utils.Log(nil, "OrderNotify-success", YouChuOrderNotifyRequest)
|
||||||
|
var orderDetail ordersmodel.Orders
|
||||||
|
var order ordersmodel.Orders
|
||||||
|
has, err := ordersmodel.GetInstance().GetDb().Where("state = ?", common.ORDER_STATUS_DEFAULT).Where("order_no = ?", YouChuOrderNotifyRequest.ReqTraceId).Get(&orderDetail)
|
||||||
|
if err != nil || !has {
|
||||||
|
return front.YouChuOrderNotifyResponse{RespCode: "000003", RespMsg: "订单不存在"}
|
||||||
|
}
|
||||||
|
var productDetail productsmodel.Products
|
||||||
|
has, err = productsmodel.GetInstance().GetDb().Where("id = ?", orderDetail.ProductId).Get(&productDetail)
|
||||||
|
if err != nil || !has {
|
||||||
|
return front.YouChuOrderNotifyResponse{RespCode: "000003", RespMsg: "商品不存在"}
|
||||||
|
}
|
||||||
|
//01-消费 03-退货
|
||||||
|
if YouChuOrderNotifyRequest.TxnFlag == "01" {
|
||||||
|
client := market.NewMarketClient(config.GetConf().OpenApiMarketConfig)
|
||||||
|
send, err := client.MarketSend(orderDetail.OrderNo, strconv.Itoa(orderDetail.VoucherId), "", "1")
|
||||||
|
utils.Log(nil, "OrderNotify-MarketSend", send)
|
||||||
|
if err != nil {
|
||||||
|
return front.YouChuOrderNotifyResponse{RespCode: "000004", RespMsg: "充值失败"}
|
||||||
|
}
|
||||||
|
order.OrgTxnSeq = YouChuOrderNotifyRequest.OrderNo
|
||||||
|
if send.ErrCode != "00" {
|
||||||
|
order.State = common.ORDER_STATUS_FAIL
|
||||||
|
} else {
|
||||||
|
order.State = common.ORDER_STATUS_FINISH
|
||||||
|
order.VoucherLink = send.Data.ShortUrl
|
||||||
|
Stock, _ := strconv.Atoi(productDetail.Stock)
|
||||||
|
productDetail.Stock = strconv.Itoa(Stock - 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//order.RefundOrderNo = YouChuOrderNotifyRequest.OrgTxnSeq
|
||||||
|
//order.RefundOrderSta = YouChuOrderNotifyRequest.OrderSta
|
||||||
|
return front.YouChuOrderNotifyResponse{RespCode: "000005", RespMsg: "订单标识错误"}
|
||||||
|
}
|
||||||
|
_, err = ordersmodel.GetInstance().GetDb().Where("order_no = ?", YouChuOrderNotifyRequest.ReqTraceId).Update(&order)
|
||||||
|
_, err = productsmodel.GetInstance().GetDb().Where("id = ?", orderDetail.ProductId).Update(&productDetail)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(nil, "OrderNotify-MarketSend", err.Error())
|
||||||
|
return front.YouChuOrderNotifyResponse{RespCode: "000004", RespMsg: "操作失败"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package services
|
package services
|
||||||
|
|
||||||
import "qteam/app/models/productsmodel"
|
import (
|
||||||
|
"qteam/app/constants/errorcode"
|
||||||
|
"qteam/app/http/entities/front"
|
||||||
|
"qteam/app/models/productsmodel"
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
func MilkProductList() (code int, productList []productsmodel.MilkProductsList) {
|
func MilkProductList() (code int, productList []productsmodel.MilkProductsList) {
|
||||||
err := productsmodel.GetInstance().GetDb().Alias("a").
|
err := productsmodel.GetInstance().GetDb().Alias("a").
|
||||||
|
@ -8,3 +13,16 @@ func MilkProductList() (code int, productList []productsmodel.MilkProductsList)
|
||||||
Where("a.status = ?", 1).Find(&productList)
|
Where("a.status = ?", 1).Find(&productList)
|
||||||
return handErr(err), productList
|
return handErr(err), productList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ProductDetail(request front.ProductDetailRequest) (code int, product productsmodel.Products) {
|
||||||
|
repo := productsmodel.GetInstance().GetDb()
|
||||||
|
conn := builder.NewCond()
|
||||||
|
if request.ProductId != 0 {
|
||||||
|
conn = conn.And(builder.Eq{"id": request.ProductId})
|
||||||
|
}
|
||||||
|
has, err := repo.Where(conn).Get(&product)
|
||||||
|
if !has {
|
||||||
|
return errorcode.NotFound, product
|
||||||
|
}
|
||||||
|
return handErr(err), product
|
||||||
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
|
|
||||||
func YouChuLogin(req *front.UnionLoginRequest) (code int, login front.LoginResponse) {
|
func YouChuLogin(req *front.UnionLoginRequest) (code int, login front.LoginResponse) {
|
||||||
client := youchu.NewYouChuClient(config.GetConf().YouChu)
|
client := youchu.NewYouChuClient(config.GetConf().YouChu)
|
||||||
YouChuResponse, err := client.Login(req.Code)
|
Code, YouChuResponse := client.Login(req.Code)
|
||||||
if err != nil {
|
if Code != errorcode.Success {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if YouChuResponse.RespCode == "000000" {
|
if YouChuResponse.RespCode == "000000" {
|
||||||
|
|
|
@ -20,15 +20,6 @@ type YouChuSendRequest struct {
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type YouChuResponse struct {
|
|
||||||
RespCode string `json:"respCode"` //000000-成功 069801-系统繁忙,请稍后重试 820007-未查询到客户号
|
|
||||||
RespMsg string `json:"respMsg"` //描 述 (失败时必填)
|
|
||||||
Phone string `json:"phone"`
|
|
||||||
IdtLastFour string `json:"idtLastFour"`
|
|
||||||
CustName string `json:"custName"`
|
|
||||||
CustFlag string `json:"custFlag"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *YouChuSendRequest) toMap() (resultMap map[string]interface{}) {
|
func (this *YouChuSendRequest) toMap() (resultMap map[string]interface{}) {
|
||||||
// Marshal the struct to JSON, ignoring omitempty fields.
|
// Marshal the struct to JSON, ignoring omitempty fields.
|
||||||
jsonBytes, err := json.Marshal(this)
|
jsonBytes, err := json.Marshal(this)
|
||||||
|
|
|
@ -19,24 +19,41 @@ func NewYouChuClient(cfg config.YouChuConfig) *YouChuClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *YouChuClient) Login(code string) (res YouChuResponse, err error) {
|
func (this *YouChuClient) Login(code string) (Code int, response front.YouChuLoginResponse) {
|
||||||
partnerTxSriNo := time.Now().Format("20060102150405") + fmt.Sprintf("%10d", mrand.Intn(10000))
|
partnerTxSriNo := time.Now().Format("20060102150405") + fmt.Sprintf("%10d", mrand.Intn(10000))
|
||||||
url := this.cfg.MerchantId + ".html?partnerTxSriNo=" + partnerTxSriNo
|
url := this.cfg.MerchantId + ".html?partnerTxSriNo=" + partnerTxSriNo
|
||||||
request := YouChuSendRequest{
|
request := front.YouChuLoginRequest{
|
||||||
CustNo: this.cfg.MerchantId,
|
Head: front.YouChuRequestHeader{
|
||||||
ReqTransTime: time.Now().Format("20060102150405"),
|
PartnerTxSriNo: partnerTxSriNo,
|
||||||
Code: code,
|
AccessType: "API",
|
||||||
|
Reserve: "",
|
||||||
|
Method: "crecard.getCustomerInfo",
|
||||||
|
Version: "1",
|
||||||
|
MerchantId: config.GetConf().YouChu.MerchantId,
|
||||||
|
AppID: config.GetConf().YouChu.AppID,
|
||||||
|
},
|
||||||
|
Body: front.YouChuLoginBody{
|
||||||
|
Code: code,
|
||||||
|
CustNo: "000000",
|
||||||
|
ReqTransTime: time.Now().Format("20060102150405"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
bytes, err := json.Marshal(request)
|
requestData := EncryptRequest(request)
|
||||||
|
bytes, err := json.Marshal(requestData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return errorcode.SystemError, response
|
||||||
}
|
}
|
||||||
data, err := this.doPost(url, partnerTxSriNo, "crecard.getCustomerInfo", bytes)
|
post, err := this.doPost(url, partnerTxSriNo, "crecard.getCustomerInfo", bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return errorcode.SystemError, response
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(data, &res)
|
responseData := DecryptResponse(string(post))
|
||||||
return
|
utils.Log(nil, "YouChuLogin", responseData)
|
||||||
|
err = json.Unmarshal([]byte(responseData), &response)
|
||||||
|
if err != nil {
|
||||||
|
return errorcode.SystemError, response
|
||||||
|
}
|
||||||
|
return errorcode.Success, response
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *YouChuClient) OrderQuery(order ordersmodel.Orders) (code int, response front.YouChuOrderQueryResponse) {
|
func (this *YouChuClient) OrderQuery(order ordersmodel.Orders) (code int, response front.YouChuOrderQueryResponse) {
|
||||||
|
@ -75,9 +92,10 @@ func (this *YouChuClient) OrderQuery(order ordersmodel.Orders) (code int, respon
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorcode.SystemError, response
|
return errorcode.SystemError, response
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(post, &response)
|
responseData := DecryptResponse(string(post))
|
||||||
|
err = json.Unmarshal([]byte(responseData), &response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorcode.SystemError, front.YouChuOrderQueryResponse{}
|
return errorcode.SystemError, response
|
||||||
}
|
}
|
||||||
return errorcode.Success, response
|
return errorcode.Success, response
|
||||||
}
|
}
|
||||||
|
@ -121,7 +139,8 @@ func (this *YouChuClient) OrderRefund(order ordersmodel.Orders) (code int, respo
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorcode.YouChuOrderRefundFail, response
|
return errorcode.YouChuOrderRefundFail, response
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(post, &response)
|
responseData := DecryptResponse(string(post))
|
||||||
|
err = json.Unmarshal(post, &responseData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorcode.SystemError, front.YouChuOrderRefundResponse{}
|
return errorcode.SystemError, front.YouChuOrderRefundResponse{}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +151,7 @@ func EncryptRequest(request interface{}) (RequestData front.YouChuRequest) {
|
||||||
input, _ := json.Marshal(request)
|
input, _ := json.Marshal(request)
|
||||||
MerchantId := config.GetConf().YouChu.MerchantId
|
MerchantId := config.GetConf().YouChu.MerchantId
|
||||||
PrivateKey := config.GetConf().Sm2.PrivateKey
|
PrivateKey := config.GetConf().Sm2.PrivateKey
|
||||||
PublicKey := config.GetConf().Sm2.PublicKey
|
PublicKey := config.GetConf().YouChu.SopPublicKey
|
||||||
encrypt, err := postbank.Encrypt(MerchantId, PrivateKey, PublicKey, string(input), "", true)
|
encrypt, err := postbank.Encrypt(MerchantId, PrivateKey, PublicKey, string(input), "", true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return front.YouChuRequest{}
|
return front.YouChuRequest{}
|
||||||
|
@ -143,3 +162,29 @@ func EncryptRequest(request interface{}) (RequestData front.YouChuRequest) {
|
||||||
}
|
}
|
||||||
return RequestData
|
return RequestData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DecryptResponse(response string) (Rsponse string) {
|
||||||
|
MerchantId := config.GetConf().YouChu.MerchantId
|
||||||
|
PrivateKey := config.GetConf().Sm2.PrivateKey
|
||||||
|
PublicKey := config.GetConf().YouChu.SopPublicKey
|
||||||
|
encrypt, err := postbank.Decrypt(MerchantId, PrivateKey, PublicKey, response, true)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var RsponseData map[string]string
|
||||||
|
err = json.Unmarshal([]byte(encrypt), &RsponseData)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
if RsponseData["body"] != "" {
|
||||||
|
Rsponse = RsponseData["body"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
utils.Log(nil, "DecryptResponse-body", Rsponse)
|
||||||
|
return Rsponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
Head string `json:"head"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
|
|
@ -42,7 +42,8 @@ type Config struct {
|
||||||
type YouChuConfig struct {
|
type YouChuConfig struct {
|
||||||
Host string
|
Host string
|
||||||
NotifyUrl string
|
NotifyUrl string
|
||||||
MilkUrl string
|
MilkUrl string //奶茶专区跳转链接
|
||||||
|
VoucherUrl string //代金券链接
|
||||||
MerchantId string //合作方编号
|
MerchantId string //合作方编号
|
||||||
MchtNo string //上单商户号
|
MchtNo string //上单商户号
|
||||||
AppID string //appid
|
AppID string //appid
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
IMAGE="msg:v1"
|
||||||
|
RPC_CONTAINER="msg"
|
||||||
|
RPC_PORT="9000"
|
||||||
|
API_PORT="8000"
|
||||||
|
V_REFLECT=""
|
||||||
|
|
||||||
|
|
||||||
|
docker build -t "${IMAGE}" . --no-cache
|
||||||
|
docker stop "${RPC_CONTAINER}"
|
||||||
|
|
||||||
|
docker rm "${RPC_CONTAINER}"
|
||||||
|
|
||||||
|
docker run -it -p "${RPC_PORT}:${RPC_PORT}" -p "${API_PORT}:${API_PORT}" --name "$RPC_CONTAINER" "${IMAGE}"
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# supervisord
|
||||||
|
supervisord -c /etc/supervisord.conf
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
; supervisor config file
|
||||||
|
|
||||||
|
[unix_http_server]
|
||||||
|
file=/var/run/supervisor.sock ; (the path to the socket file)
|
||||||
|
chmod=0700 ; sockef file mode (default 0700)
|
||||||
|
|
||||||
|
[supervisord]
|
||||||
|
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
|
||||||
|
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
|
||||||
|
childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP)
|
||||||
|
|
||||||
|
; the below section must remain in the config file for RPC
|
||||||
|
; (supervisorctl/web interface) to work, additional interfaces may be
|
||||||
|
; added by defining them in separate rpcinterface: sections
|
||||||
|
[rpcinterface:supervisor]
|
||||||
|
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||||||
|
|
||||||
|
[supervisorctl]
|
||||||
|
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket
|
||||||
|
|
||||||
|
; The [include] section can just contain the "files" setting. This
|
||||||
|
; setting can list multiple files (separated by whitespace or
|
||||||
|
; newlines). It can also contain wildcards. The filenames are
|
||||||
|
; interpreted as relative to this file. Included files *cannot*
|
||||||
|
; include files themselves.
|
||||||
|
|
||||||
|
|
||||||
|
[include]
|
||||||
|
files = /msgc/sh/supervisord_include/*.conf
|
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
[program:rpc]
|
||||||
|
directory=/msgc/cmd/rpc
|
||||||
|
# 执行的命令
|
||||||
|
command=/msgc/cmd/rpc/msgcenter
|
||||||
|
#在 supervisord 启动的时候也自动启动
|
||||||
|
autorstart=false
|
||||||
|
#程序异常退出后自动重启
|
||||||
|
autorestart=true
|
||||||
|
#启动 5 秒后没有异常退出,就当作已经正常启动了
|
||||||
|
startsecs=5
|
||||||
|
#启动失败自动重试次数,默认是 3
|
||||||
|
startretries=3
|
||||||
|
#把 stderr 重定向到 stdout,默认 false
|
||||||
|
redirect_stderr=false
|
||||||
|
#stdout 日志文件大小,默认 50MB
|
||||||
|
stdout_logfile_maxbytes = 20MB
|
||||||
|
#stdout 日志文件备份数
|
||||||
|
stdout_logfile_backups = 20
|
||||||
|
#stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
|
||||||
|
stdout_logfile=/var/log/supervisor_swoole_http_out.log
|
||||||
|
stderr_logfile=/var/log/supervisor_swoole_http_err.log
|
||||||
|
|
||||||
|
[program:api]
|
||||||
|
directory=/msgc/cmd/api
|
||||||
|
# 执行的命令
|
||||||
|
command=/msgc/cmd/api/msgcenter
|
||||||
|
#在 supervisord 启动的时候也自动启动
|
||||||
|
autorstart=false
|
||||||
|
#程序异常退出后自动重启
|
||||||
|
autorestart=true
|
||||||
|
#启动 5 秒后没有异常退出,就当作已经正常启动了
|
||||||
|
startsecs=5
|
||||||
|
#启动失败自动重试次数,默认是 3
|
||||||
|
startretries=3
|
||||||
|
#把 stderr 重定向到 stdout,默认 false
|
||||||
|
redirect_stderr=false
|
||||||
|
#stdout 日志文件大小,默认 50MB
|
||||||
|
stdout_logfile_maxbytes = 20MB
|
||||||
|
#stdout 日志文件备份数
|
||||||
|
stdout_logfile_backups = 20
|
||||||
|
#stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
|
||||||
|
stdout_logfile=/var/log/supervisor_swoole_http_out.log
|
||||||
|
stderr_logfile=/var/log/supervisor_swoole_http_err.log
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 检测 MySQL 容器是否在运行
|
||||||
|
if [ "$(docker ps -q -f name=mysql-container)" ] && [ "$(docker ps -q -f name=redis-container)" ]; then
|
||||||
|
echo "MySQL 容器已经在运行,退出脚本。"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 下载 MySQL 8.0.33 镜像
|
||||||
|
docker pull mysql:8.0.33
|
||||||
|
|
||||||
|
# 下载 Redis Alpine 3.10 镜像
|
||||||
|
docker pull redis:alpine3.10
|
||||||
|
|
||||||
|
# 挂载 Redis 和 MySQL 配置目录以及数据存储目录到 /var/www 目录
|
||||||
|
mkdir -p /var/www/redis/config
|
||||||
|
mkdir -p /var/www/mysql/config
|
||||||
|
mkdir -p /var/www/mysql/data
|
||||||
|
|
||||||
|
# 启动 MySQL Docker 容器,并挂载配置目录和数据存储目录,设置 root 密码为 lansexiongdi@666,并开启远程连接
|
||||||
|
# 在容器内部创建新的 MySQL 配置目录
|
||||||
|
docker run -d --name mysql-container -v /var/www/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=lansexiongdi@666 -e MYSQL_ROOT_HOST='%' -p 3306:3306 mysql:8.0.33
|
||||||
|
# 等待 MySQL 容器启动
|
||||||
|
echo "等待 MySQL 容器启动..."
|
||||||
|
|
||||||
|
# 给 MySQL 的 root 用户授权远程登录
|
||||||
|
docker exec -it mysql-container mysql -uroot -p lansexiongdi@666 -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'lansexiongdi@666';SET GLOBAL log_bin = 'mysql-bin'; FLUSH PRIVILEGES;"
|
||||||
|
|
||||||
|
# 启动 Redis Docker 容器,并挂载配置目录,设置密码为 lansexiongdi@666
|
||||||
|
docker run -d --name redis-container -v /var/www/redis/redis.conf:/etc/redis.conf -v /var/www/redis/data:/var/lib/redis -e REDIS_PASSWORD=lansexiongdi@666 -p 6379:6379 redis:alpine3.10
|
||||||
|
|
||||||
|
echo "MySQL 和 Redis 容器已启动,并配置目录已挂载到 /var/www 目录。MySQL root 用户已授权远程登录。"
|
Loading…
Reference in New Issue