Merge pull request #12 from rede5/codex/implementar-crud-nas-rotas-e-documentar
Add CRUD endpoints for companies, products and orders; update repo/service/handlers and regenerate Swagger
This commit is contained in:
commit
1309a120cb
7 changed files with 3227 additions and 0 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,52 @@
|
|||
basePath: /
|
||||
definitions:
|
||||
domain.AdminDashboard:
|
||||
properties:
|
||||
gmv_cents:
|
||||
type: integer
|
||||
new_companies:
|
||||
type: integer
|
||||
window_start_at:
|
||||
type: string
|
||||
type: object
|
||||
domain.CartItem:
|
||||
properties:
|
||||
batch:
|
||||
type: string
|
||||
buyer_id:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
expires_at:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
product_id:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
quantity:
|
||||
type: integer
|
||||
unit_cents:
|
||||
type: integer
|
||||
updated_at:
|
||||
type: string
|
||||
type: object
|
||||
domain.CartSummary:
|
||||
properties:
|
||||
discount_cents:
|
||||
type: integer
|
||||
discount_reason:
|
||||
type: string
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/domain.CartItem'
|
||||
type: array
|
||||
subtotal_cents:
|
||||
type: integer
|
||||
total_cents:
|
||||
type: integer
|
||||
type: object
|
||||
domain.Company:
|
||||
properties:
|
||||
cnpj:
|
||||
|
|
@ -20,6 +67,34 @@ definitions:
|
|||
updated_at:
|
||||
type: string
|
||||
type: object
|
||||
domain.CompanyRating:
|
||||
properties:
|
||||
average_score:
|
||||
type: number
|
||||
company_id:
|
||||
type: string
|
||||
total_reviews:
|
||||
type: integer
|
||||
type: object
|
||||
domain.InventoryItem:
|
||||
properties:
|
||||
batch:
|
||||
type: string
|
||||
expires_at:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
price_cents:
|
||||
type: integer
|
||||
product_id:
|
||||
type: string
|
||||
quantity:
|
||||
type: integer
|
||||
seller_id:
|
||||
type: string
|
||||
updated_at:
|
||||
type: string
|
||||
type: object
|
||||
domain.Order:
|
||||
properties:
|
||||
buyer_id:
|
||||
|
|
@ -140,6 +215,40 @@ definitions:
|
|||
updated_at:
|
||||
type: string
|
||||
type: object
|
||||
domain.Review:
|
||||
properties:
|
||||
buyer_id:
|
||||
type: string
|
||||
comment:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
order_id:
|
||||
type: string
|
||||
rating:
|
||||
type: integer
|
||||
seller_id:
|
||||
type: string
|
||||
type: object
|
||||
domain.SellerDashboard:
|
||||
properties:
|
||||
low_stock_alerts:
|
||||
items:
|
||||
$ref: '#/definitions/domain.Product'
|
||||
type: array
|
||||
orders_count:
|
||||
type: integer
|
||||
seller_id:
|
||||
type: string
|
||||
top_products:
|
||||
items:
|
||||
$ref: '#/definitions/domain.TopProduct'
|
||||
type: array
|
||||
total_sales_cents:
|
||||
type: integer
|
||||
type: object
|
||||
domain.Shipment:
|
||||
properties:
|
||||
carrier:
|
||||
|
|
@ -180,6 +289,17 @@ definitions:
|
|||
zip_code:
|
||||
type: string
|
||||
type: object
|
||||
domain.TopProduct:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
product_id:
|
||||
type: string
|
||||
revenue_cents:
|
||||
type: integer
|
||||
total_quantity:
|
||||
type: integer
|
||||
type: object
|
||||
domain.User:
|
||||
properties:
|
||||
company_id:
|
||||
|
|
@ -210,6 +330,13 @@ definitions:
|
|||
$ref: '#/definitions/domain.User'
|
||||
type: array
|
||||
type: object
|
||||
handler.addCartItemRequest:
|
||||
properties:
|
||||
product_id:
|
||||
type: string
|
||||
quantity:
|
||||
type: integer
|
||||
type: object
|
||||
handler.authResponse:
|
||||
properties:
|
||||
expires_at:
|
||||
|
|
@ -230,6 +357,15 @@ definitions:
|
|||
shipping:
|
||||
$ref: '#/definitions/domain.ShippingAddress'
|
||||
type: object
|
||||
handler.createReviewRequest:
|
||||
properties:
|
||||
comment:
|
||||
type: string
|
||||
order_id:
|
||||
type: string
|
||||
rating:
|
||||
type: integer
|
||||
type: object
|
||||
handler.createShipmentRequest:
|
||||
properties:
|
||||
carrier:
|
||||
|
|
@ -254,6 +390,15 @@ definitions:
|
|||
role:
|
||||
type: string
|
||||
type: object
|
||||
handler.inventoryAdjustRequest:
|
||||
properties:
|
||||
delta:
|
||||
type: integer
|
||||
product_id:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
type: object
|
||||
handler.loginRequest:
|
||||
properties:
|
||||
email:
|
||||
|
|
@ -317,6 +462,36 @@ definitions:
|
|||
stock:
|
||||
type: integer
|
||||
type: object
|
||||
handler.updateCompanyRequest:
|
||||
properties:
|
||||
cnpj:
|
||||
type: string
|
||||
corporate_name:
|
||||
type: string
|
||||
is_verified:
|
||||
type: boolean
|
||||
license_number:
|
||||
type: string
|
||||
role:
|
||||
type: string
|
||||
type: object
|
||||
handler.updateProductRequest:
|
||||
properties:
|
||||
batch:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
expires_at:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
price_cents:
|
||||
type: integer
|
||||
seller_id:
|
||||
type: string
|
||||
stock:
|
||||
type: integer
|
||||
type: object
|
||||
handler.updateStatusRequest:
|
||||
properties:
|
||||
status:
|
||||
|
|
@ -380,7 +555,108 @@ paths:
|
|||
summary: Registro de empresas
|
||||
tags:
|
||||
- Empresas
|
||||
/api/companies/{id}:
|
||||
delete:
|
||||
parameters:
|
||||
- description: Company ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Remover empresa
|
||||
tags:
|
||||
- Empresas
|
||||
get:
|
||||
parameters:
|
||||
- description: Company ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Company'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Obter empresa
|
||||
tags:
|
||||
- Empresas
|
||||
patch:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: Company ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Campos para atualização
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handler.updateCompanyRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Company'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Atualizar empresa
|
||||
tags:
|
||||
- Empresas
|
||||
/api/orders:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/domain.Order'
|
||||
type: array
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Listar pedidos
|
||||
tags:
|
||||
- Pedidos
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
|
|
@ -402,6 +678,33 @@ paths:
|
|||
tags:
|
||||
- Pedidos
|
||||
/api/orders/{id}:
|
||||
delete:
|
||||
parameters:
|
||||
- description: Order ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Remover pedido
|
||||
tags:
|
||||
- Pedidos
|
||||
get:
|
||||
parameters:
|
||||
- description: Order ID
|
||||
|
|
@ -416,6 +719,8 @@ paths:
|
|||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Order'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Consulta pedido
|
||||
tags:
|
||||
- Pedidos
|
||||
|
|
@ -434,6 +739,8 @@ paths:
|
|||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/domain.PaymentPreference'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Cria preferência de pagamento Mercado Pago com split nativo
|
||||
tags:
|
||||
- Pagamentos
|
||||
|
|
@ -458,6 +765,8 @@ paths:
|
|||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Atualiza status do pedido
|
||||
tags:
|
||||
- Pedidos
|
||||
|
|
@ -495,6 +804,92 @@ paths:
|
|||
summary: Cadastro de produto com rastreabilidade de lote
|
||||
tags:
|
||||
- Produtos
|
||||
/api/products/{id}:
|
||||
delete:
|
||||
parameters:
|
||||
- description: Product ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"204":
|
||||
description: ""
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Remover produto
|
||||
tags:
|
||||
- Produtos
|
||||
get:
|
||||
parameters:
|
||||
- description: Product ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Product'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Obter produto
|
||||
tags:
|
||||
- Produtos
|
||||
patch:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: Product ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Campos para atualização
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handler.updateProductRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Product'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Atualizar produto
|
||||
tags:
|
||||
- Produtos
|
||||
/api/v1/auth/login:
|
||||
post:
|
||||
consumes:
|
||||
|
|
@ -563,6 +958,205 @@ paths:
|
|||
summary: Cadastro de usuário
|
||||
tags:
|
||||
- Autenticação
|
||||
/api/v1/cart:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.CartSummary'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Obter carrinho
|
||||
tags:
|
||||
- Carrinho
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: Item do carrinho
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handler.addCartItemRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/domain.CartSummary'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Adicionar item ao carrinho
|
||||
tags:
|
||||
- Carrinho
|
||||
/api/v1/cart/{id}:
|
||||
delete:
|
||||
parameters:
|
||||
- description: Cart item ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.CartSummary'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Remover item do carrinho
|
||||
tags:
|
||||
- Carrinho
|
||||
/api/v1/companies/{id}/rating:
|
||||
get:
|
||||
parameters:
|
||||
- description: Company ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.CompanyRating'
|
||||
summary: Obter avaliação da empresa
|
||||
tags:
|
||||
- Empresas
|
||||
/api/v1/companies/{id}/verify:
|
||||
patch:
|
||||
parameters:
|
||||
- description: Company ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Company'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Verificar empresa
|
||||
tags:
|
||||
- Empresas
|
||||
/api/v1/companies/me:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Company'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Obter minha empresa
|
||||
tags:
|
||||
- Empresas
|
||||
/api/v1/dashboard/admin:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.AdminDashboard'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Dashboard do administrador
|
||||
tags:
|
||||
- Dashboard
|
||||
/api/v1/dashboard/seller:
|
||||
get:
|
||||
parameters:
|
||||
- description: Seller ID
|
||||
in: query
|
||||
name: seller_id
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.SellerDashboard'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Dashboard do vendedor
|
||||
tags:
|
||||
- Dashboard
|
||||
/api/v1/inventory:
|
||||
get:
|
||||
parameters:
|
||||
- description: Dias para expiração
|
||||
in: query
|
||||
name: expires_in_days
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/domain.InventoryItem'
|
||||
type: array
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Listar estoque
|
||||
tags:
|
||||
- Estoque
|
||||
/api/v1/inventory/adjust:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: Ajuste de estoque
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handler.inventoryAdjustRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.InventoryItem'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Ajustar estoque
|
||||
tags:
|
||||
- Estoque
|
||||
/api/v1/payments/webhook:
|
||||
post:
|
||||
consumes:
|
||||
|
|
@ -584,6 +1178,35 @@ paths:
|
|||
summary: Recebe notificações do Mercado Pago
|
||||
tags:
|
||||
- Pagamentos
|
||||
/api/v1/reviews:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: Dados da avaliação
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handler.createReviewRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Review'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Criar avaliação
|
||||
tags:
|
||||
- Avaliações
|
||||
/api/v1/shipments:
|
||||
post:
|
||||
consumes:
|
||||
|
|
@ -602,6 +1225,8 @@ paths:
|
|||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Shipment'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Gera guia de postagem/transporte
|
||||
tags:
|
||||
- Logistica
|
||||
|
|
@ -620,6 +1245,8 @@ paths:
|
|||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/domain.Shipment'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Rastreia entrega
|
||||
tags:
|
||||
- Logistica
|
||||
|
|
|
|||
|
|
@ -160,6 +160,114 @@ func (h *Handler) ListCompanies(w http.ResponseWriter, r *http.Request) {
|
|||
writeJSON(w, http.StatusOK, companies)
|
||||
}
|
||||
|
||||
// GetCompany godoc
|
||||
// @Summary Obter empresa
|
||||
// @Tags Empresas
|
||||
// @Produce json
|
||||
// @Param id path string true "Company ID"
|
||||
// @Success 200 {object} domain.Company
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /api/companies/{id} [get]
|
||||
func (h *Handler) GetCompany(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := parseUUIDFromPath(r.URL.Path)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
company, err := h.svc.GetCompany(r.Context(), id)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, company)
|
||||
}
|
||||
|
||||
// UpdateCompany godoc
|
||||
// @Summary Atualizar empresa
|
||||
// @Tags Empresas
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Company ID"
|
||||
// @Param payload body updateCompanyRequest true "Campos para atualização"
|
||||
// @Success 200 {object} domain.Company
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /api/companies/{id} [patch]
|
||||
func (h *Handler) UpdateCompany(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := parseUUIDFromPath(r.URL.Path)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
var req updateCompanyRequest
|
||||
if err := decodeJSON(r.Context(), r, &req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
company, err := h.svc.GetCompany(r.Context(), id)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Role != nil {
|
||||
company.Role = *req.Role
|
||||
}
|
||||
if req.CNPJ != nil {
|
||||
company.CNPJ = *req.CNPJ
|
||||
}
|
||||
if req.CorporateName != nil {
|
||||
company.CorporateName = *req.CorporateName
|
||||
}
|
||||
if req.LicenseNumber != nil {
|
||||
company.LicenseNumber = *req.LicenseNumber
|
||||
}
|
||||
if req.IsVerified != nil {
|
||||
company.IsVerified = *req.IsVerified
|
||||
}
|
||||
|
||||
if err := h.svc.UpdateCompany(r.Context(), company); err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, company)
|
||||
}
|
||||
|
||||
// DeleteCompany godoc
|
||||
// @Summary Remover empresa
|
||||
// @Tags Empresas
|
||||
// @Param id path string true "Company ID"
|
||||
// @Success 204 ""
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /api/companies/{id} [delete]
|
||||
func (h *Handler) DeleteCompany(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := parseUUIDFromPath(r.URL.Path)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.DeleteCompany(r.Context(), id); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// VerifyCompany godoc
|
||||
// @Summary Verificar empresa
|
||||
// @Tags Empresas
|
||||
// @Security BearerAuth
|
||||
// @Param id path string true "Company ID"
|
||||
// @Success 200 {object} domain.Company
|
||||
// @Router /api/v1/companies/{id}/verify [patch]
|
||||
// VerifyCompany toggles the verification flag for a company (admin only).
|
||||
func (h *Handler) VerifyCompany(w http.ResponseWriter, r *http.Request) {
|
||||
if !strings.HasSuffix(r.URL.Path, "/verify") {
|
||||
|
|
@ -182,6 +290,13 @@ func (h *Handler) VerifyCompany(w http.ResponseWriter, r *http.Request) {
|
|||
writeJSON(w, http.StatusOK, company)
|
||||
}
|
||||
|
||||
// GetMyCompany godoc
|
||||
// @Summary Obter minha empresa
|
||||
// @Tags Empresas
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Success 200 {object} domain.Company
|
||||
// @Router /api/v1/companies/me [get]
|
||||
// GetMyCompany returns the company linked to the authenticated user.
|
||||
func (h *Handler) GetMyCompany(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := middleware.GetClaims(r.Context())
|
||||
|
|
@ -199,6 +314,13 @@ func (h *Handler) GetMyCompany(w http.ResponseWriter, r *http.Request) {
|
|||
writeJSON(w, http.StatusOK, company)
|
||||
}
|
||||
|
||||
// GetCompanyRating godoc
|
||||
// @Summary Obter avaliação da empresa
|
||||
// @Tags Empresas
|
||||
// @Produce json
|
||||
// @Param id path string true "Company ID"
|
||||
// @Success 200 {object} domain.CompanyRating
|
||||
// @Router /api/v1/companies/{id}/rating [get]
|
||||
// GetCompanyRating exposes the average score for a company.
|
||||
func (h *Handler) GetCompanyRating(w http.ResponseWriter, r *http.Request) {
|
||||
if !strings.HasSuffix(r.URL.Path, "/rating") {
|
||||
|
|
@ -269,6 +391,121 @@ func (h *Handler) ListProducts(w http.ResponseWriter, r *http.Request) {
|
|||
writeJSON(w, http.StatusOK, products)
|
||||
}
|
||||
|
||||
// GetProduct godoc
|
||||
// @Summary Obter produto
|
||||
// @Tags Produtos
|
||||
// @Produce json
|
||||
// @Param id path string true "Product ID"
|
||||
// @Success 200 {object} domain.Product
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /api/products/{id} [get]
|
||||
func (h *Handler) GetProduct(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := parseUUIDFromPath(r.URL.Path)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
product, err := h.svc.GetProduct(r.Context(), id)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, product)
|
||||
}
|
||||
|
||||
// UpdateProduct godoc
|
||||
// @Summary Atualizar produto
|
||||
// @Tags Produtos
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Product ID"
|
||||
// @Param payload body updateProductRequest true "Campos para atualização"
|
||||
// @Success 200 {object} domain.Product
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /api/products/{id} [patch]
|
||||
func (h *Handler) UpdateProduct(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := parseUUIDFromPath(r.URL.Path)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
var req updateProductRequest
|
||||
if err := decodeJSON(r.Context(), r, &req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
product, err := h.svc.GetProduct(r.Context(), id)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.SellerID != nil {
|
||||
product.SellerID = *req.SellerID
|
||||
}
|
||||
if req.Name != nil {
|
||||
product.Name = *req.Name
|
||||
}
|
||||
if req.Description != nil {
|
||||
product.Description = *req.Description
|
||||
}
|
||||
if req.Batch != nil {
|
||||
product.Batch = *req.Batch
|
||||
}
|
||||
if req.ExpiresAt != nil {
|
||||
product.ExpiresAt = *req.ExpiresAt
|
||||
}
|
||||
if req.PriceCents != nil {
|
||||
product.PriceCents = *req.PriceCents
|
||||
}
|
||||
if req.Stock != nil {
|
||||
product.Stock = *req.Stock
|
||||
}
|
||||
|
||||
if err := h.svc.UpdateProduct(r.Context(), product); err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, product)
|
||||
}
|
||||
|
||||
// DeleteProduct godoc
|
||||
// @Summary Remover produto
|
||||
// @Tags Produtos
|
||||
// @Param id path string true "Product ID"
|
||||
// @Success 204 ""
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /api/products/{id} [delete]
|
||||
func (h *Handler) DeleteProduct(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := parseUUIDFromPath(r.URL.Path)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.DeleteProduct(r.Context(), id); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// ListInventory godoc
|
||||
// @Summary Listar estoque
|
||||
// @Tags Estoque
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param expires_in_days query int false "Dias para expiração"
|
||||
// @Success 200 {array} domain.InventoryItem
|
||||
// @Router /api/v1/inventory [get]
|
||||
// ListInventory exposes stock with expiring batch filters.
|
||||
func (h *Handler) ListInventory(w http.ResponseWriter, r *http.Request) {
|
||||
var filter domain.InventoryFilter
|
||||
|
|
@ -291,6 +528,16 @@ func (h *Handler) ListInventory(w http.ResponseWriter, r *http.Request) {
|
|||
writeJSON(w, http.StatusOK, inventory)
|
||||
}
|
||||
|
||||
// AdjustInventory godoc
|
||||
// @Summary Ajustar estoque
|
||||
// @Tags Estoque
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param payload body inventoryAdjustRequest true "Ajuste de estoque"
|
||||
// @Success 200 {object} domain.InventoryItem
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Router /api/v1/inventory/adjust [post]
|
||||
// AdjustInventory handles manual stock corrections.
|
||||
func (h *Handler) AdjustInventory(w http.ResponseWriter, r *http.Request) {
|
||||
var req inventoryAdjustRequest
|
||||
|
|
@ -349,9 +596,27 @@ func (h *Handler) CreateOrder(w http.ResponseWriter, r *http.Request) {
|
|||
writeJSON(w, http.StatusCreated, order)
|
||||
}
|
||||
|
||||
// ListOrders godoc
|
||||
// @Summary Listar pedidos
|
||||
// @Tags Pedidos
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Success 200 {array} domain.Order
|
||||
// @Router /api/orders [get]
|
||||
func (h *Handler) ListOrders(w http.ResponseWriter, r *http.Request) {
|
||||
orders, err := h.svc.ListOrders(r.Context())
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, orders)
|
||||
}
|
||||
|
||||
// GetOrder godoc
|
||||
// @Summary Consulta pedido
|
||||
// @Tags Pedidos
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param id path string true "Order ID"
|
||||
// @Success 200 {object} domain.Order
|
||||
|
|
@ -375,6 +640,7 @@ func (h *Handler) GetOrder(w http.ResponseWriter, r *http.Request) {
|
|||
// UpdateOrderStatus godoc
|
||||
// @Summary Atualiza status do pedido
|
||||
// @Tags Pedidos
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Order ID"
|
||||
|
|
@ -407,6 +673,40 @@ func (h *Handler) UpdateOrderStatus(w http.ResponseWriter, r *http.Request) {
|
|||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// DeleteOrder godoc
|
||||
// @Summary Remover pedido
|
||||
// @Tags Pedidos
|
||||
// @Security BearerAuth
|
||||
// @Param id path string true "Order ID"
|
||||
// @Success 204 ""
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /api/orders/{id} [delete]
|
||||
func (h *Handler) DeleteOrder(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := parseUUIDFromPath(r.URL.Path)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.DeleteOrder(r.Context(), id); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// CreateReview godoc
|
||||
// @Summary Criar avaliação
|
||||
// @Tags Avaliações
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param payload body createReviewRequest true "Dados da avaliação"
|
||||
// @Success 201 {object} domain.Review
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Router /api/v1/reviews [post]
|
||||
// CreateReview allows buyers to rate the seller after delivery.
|
||||
func (h *Handler) CreateReview(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := middleware.GetClaims(r.Context())
|
||||
|
|
@ -430,6 +730,16 @@ func (h *Handler) CreateReview(w http.ResponseWriter, r *http.Request) {
|
|||
writeJSON(w, http.StatusCreated, review)
|
||||
}
|
||||
|
||||
// AddToCart godoc
|
||||
// @Summary Adicionar item ao carrinho
|
||||
// @Tags Carrinho
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param payload body addCartItemRequest true "Item do carrinho"
|
||||
// @Success 201 {object} domain.CartSummary
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Router /api/v1/cart [post]
|
||||
// AddToCart appends an item to the authenticated buyer cart respecting stock.
|
||||
func (h *Handler) AddToCart(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := middleware.GetClaims(r.Context())
|
||||
|
|
@ -453,6 +763,13 @@ func (h *Handler) AddToCart(w http.ResponseWriter, r *http.Request) {
|
|||
writeJSON(w, http.StatusCreated, summary)
|
||||
}
|
||||
|
||||
// GetCart godoc
|
||||
// @Summary Obter carrinho
|
||||
// @Tags Carrinho
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Success 200 {object} domain.CartSummary
|
||||
// @Router /api/v1/cart [get]
|
||||
// GetCart returns cart contents and totals for the authenticated buyer.
|
||||
func (h *Handler) GetCart(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := middleware.GetClaims(r.Context())
|
||||
|
|
@ -469,6 +786,14 @@ func (h *Handler) GetCart(w http.ResponseWriter, r *http.Request) {
|
|||
writeJSON(w, http.StatusOK, summary)
|
||||
}
|
||||
|
||||
// DeleteCartItem godoc
|
||||
// @Summary Remover item do carrinho
|
||||
// @Tags Carrinho
|
||||
// @Security BearerAuth
|
||||
// @Param id path string true "Cart item ID"
|
||||
// @Success 200 {object} domain.CartSummary
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Router /api/v1/cart/{id} [delete]
|
||||
// DeleteCartItem removes a product from the cart and returns the updated totals.
|
||||
func (h *Handler) DeleteCartItem(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := middleware.GetClaims(r.Context())
|
||||
|
|
@ -495,6 +820,7 @@ func (h *Handler) DeleteCartItem(w http.ResponseWriter, r *http.Request) {
|
|||
// CreatePaymentPreference godoc
|
||||
// @Summary Cria preferência de pagamento Mercado Pago com split nativo
|
||||
// @Tags Pagamentos
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param id path string true "Order ID"
|
||||
// @Success 201 {object} domain.PaymentPreference
|
||||
|
|
@ -523,6 +849,7 @@ func (h *Handler) CreatePaymentPreference(w http.ResponseWriter, r *http.Request
|
|||
// CreateShipment godoc
|
||||
// @Summary Gera guia de postagem/transporte
|
||||
// @Tags Logistica
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param shipment body createShipmentRequest true "Dados de envio"
|
||||
|
|
@ -553,6 +880,7 @@ func (h *Handler) CreateShipment(w http.ResponseWriter, r *http.Request) {
|
|||
// GetShipmentByOrderID godoc
|
||||
// @Summary Rastreia entrega
|
||||
// @Tags Logistica
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param order_id path string true "Order ID"
|
||||
// @Success 200 {object} domain.Shipment
|
||||
|
|
@ -598,6 +926,13 @@ func (h *Handler) HandlePaymentWebhook(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// GetSellerDashboard aggregates KPIs for the authenticated seller or the requested company.
|
||||
// @Summary Dashboard do vendedor
|
||||
// @Tags Dashboard
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param seller_id query string false "Seller ID"
|
||||
// @Success 200 {object} domain.SellerDashboard
|
||||
// @Router /api/v1/dashboard/seller [get]
|
||||
func (h *Handler) GetSellerDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
requester, err := getRequester(r)
|
||||
if err != nil {
|
||||
|
|
@ -635,6 +970,12 @@ func (h *Handler) GetSellerDashboard(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// GetAdminDashboard exposes platform-wide aggregated metrics.
|
||||
// @Summary Dashboard do administrador
|
||||
// @Tags Dashboard
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Success 200 {object} domain.AdminDashboard
|
||||
// @Router /api/v1/dashboard/admin [get]
|
||||
func (h *Handler) GetAdminDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
requester, err := getRequester(r)
|
||||
if err != nil {
|
||||
|
|
@ -1024,6 +1365,14 @@ type registerCompanyRequest struct {
|
|||
LicenseNumber string `json:"license_number"`
|
||||
}
|
||||
|
||||
type updateCompanyRequest struct {
|
||||
Role *string `json:"role,omitempty"`
|
||||
CNPJ *string `json:"cnpj,omitempty"`
|
||||
CorporateName *string `json:"corporate_name,omitempty"`
|
||||
LicenseNumber *string `json:"license_number,omitempty"`
|
||||
IsVerified *bool `json:"is_verified,omitempty"`
|
||||
}
|
||||
|
||||
type registerProductRequest struct {
|
||||
SellerID uuid.UUID `json:"seller_id"`
|
||||
Name string `json:"name"`
|
||||
|
|
@ -1034,6 +1383,16 @@ type registerProductRequest struct {
|
|||
Stock int64 `json:"stock"`
|
||||
}
|
||||
|
||||
type updateProductRequest struct {
|
||||
SellerID *uuid.UUID `json:"seller_id,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Batch *string `json:"batch,omitempty"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||
PriceCents *int64 `json:"price_cents,omitempty"`
|
||||
Stock *int64 `json:"stock,omitempty"`
|
||||
}
|
||||
|
||||
type createOrderRequest struct {
|
||||
BuyerID uuid.UUID `json:"buyer_id"`
|
||||
SellerID uuid.UUID `json:"seller_id"`
|
||||
|
|
|
|||
|
|
@ -74,6 +74,53 @@ WHERE id = :id`
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) DeleteCompany(ctx context.Context, id uuid.UUID) error {
|
||||
var count int
|
||||
if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM users WHERE company_id = $1`, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("company has related users")
|
||||
}
|
||||
if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM products WHERE seller_id = $1`, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("company has related products")
|
||||
}
|
||||
if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM orders WHERE buyer_id = $1 OR seller_id = $1`, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("company has related orders")
|
||||
}
|
||||
if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM reviews WHERE buyer_id = $1 OR seller_id = $1`, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("company has related reviews")
|
||||
}
|
||||
if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM cart_items WHERE buyer_id = $1`, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("company has related cart items")
|
||||
}
|
||||
|
||||
res, err := r.db.ExecContext(ctx, `DELETE FROM companies WHERE id = $1`, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rows == 0 {
|
||||
return errors.New("company not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) CreateProduct(ctx context.Context, product *domain.Product) error {
|
||||
now := time.Now().UTC()
|
||||
product.CreatedAt = now
|
||||
|
|
@ -104,6 +151,68 @@ func (r *Repository) GetProduct(ctx context.Context, id uuid.UUID) (*domain.Prod
|
|||
return &product, nil
|
||||
}
|
||||
|
||||
func (r *Repository) UpdateProduct(ctx context.Context, product *domain.Product) error {
|
||||
product.UpdatedAt = time.Now().UTC()
|
||||
|
||||
query := `UPDATE products
|
||||
SET seller_id = :seller_id, name = :name, description = :description, batch = :batch, expires_at = :expires_at, price_cents = :price_cents, stock = :stock, updated_at = :updated_at
|
||||
WHERE id = :id`
|
||||
|
||||
res, err := r.db.NamedExecContext(ctx, query, product)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rows == 0 {
|
||||
return errors.New("product not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) DeleteProduct(ctx context.Context, id uuid.UUID) error {
|
||||
var count int
|
||||
if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM order_items WHERE product_id = $1`, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("product has related orders")
|
||||
}
|
||||
|
||||
tx, err := r.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM inventory_adjustments WHERE product_id = $1`, id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM cart_items WHERE product_id = $1`, id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := tx.ExecContext(ctx, `DELETE FROM products WHERE id = $1`, id)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if rows == 0 {
|
||||
_ = tx.Rollback()
|
||||
return errors.New("product not found")
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *Repository) CreateOrder(ctx context.Context, order *domain.Order) error {
|
||||
now := time.Now().UTC()
|
||||
order.CreatedAt = now
|
||||
|
|
@ -135,6 +244,62 @@ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)`
|
|||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *Repository) ListOrders(ctx context.Context) ([]domain.Order, error) {
|
||||
var rows []struct {
|
||||
ID uuid.UUID `db:"id"`
|
||||
BuyerID uuid.UUID `db:"buyer_id"`
|
||||
SellerID uuid.UUID `db:"seller_id"`
|
||||
Status domain.OrderStatus `db:"status"`
|
||||
TotalCents int64 `db:"total_cents"`
|
||||
ShippingRecipientName string `db:"shipping_recipient_name"`
|
||||
ShippingStreet string `db:"shipping_street"`
|
||||
ShippingNumber string `db:"shipping_number"`
|
||||
ShippingComplement string `db:"shipping_complement"`
|
||||
ShippingDistrict string `db:"shipping_district"`
|
||||
ShippingCity string `db:"shipping_city"`
|
||||
ShippingState string `db:"shipping_state"`
|
||||
ShippingZipCode string `db:"shipping_zip_code"`
|
||||
ShippingCountry string `db:"shipping_country"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}
|
||||
query := `SELECT id, buyer_id, seller_id, status, total_cents, shipping_recipient_name, shipping_street, shipping_number, shipping_complement, shipping_district, shipping_city, shipping_state, shipping_zip_code, shipping_country, created_at, updated_at FROM orders ORDER BY created_at DESC`
|
||||
if err := r.db.SelectContext(ctx, &rows, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orders := make([]domain.Order, 0, len(rows))
|
||||
itemQuery := `SELECT id, order_id, product_id, quantity, unit_cents, batch, expires_at FROM order_items WHERE order_id = $1`
|
||||
for _, row := range rows {
|
||||
var items []domain.OrderItem
|
||||
if err := r.db.SelectContext(ctx, &items, itemQuery, row.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orders = append(orders, domain.Order{
|
||||
ID: row.ID,
|
||||
BuyerID: row.BuyerID,
|
||||
SellerID: row.SellerID,
|
||||
Status: row.Status,
|
||||
TotalCents: row.TotalCents,
|
||||
Items: items,
|
||||
Shipping: domain.ShippingAddress{
|
||||
RecipientName: row.ShippingRecipientName,
|
||||
Street: row.ShippingStreet,
|
||||
Number: row.ShippingNumber,
|
||||
Complement: row.ShippingComplement,
|
||||
District: row.ShippingDistrict,
|
||||
City: row.ShippingCity,
|
||||
State: row.ShippingState,
|
||||
ZipCode: row.ShippingZipCode,
|
||||
Country: row.ShippingCountry,
|
||||
},
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
})
|
||||
}
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error) {
|
||||
var row struct {
|
||||
ID uuid.UUID `db:"id"`
|
||||
|
|
@ -204,6 +369,43 @@ func (r *Repository) UpdateOrderStatus(ctx context.Context, id uuid.UUID, status
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) DeleteOrder(ctx context.Context, id uuid.UUID) error {
|
||||
tx, err := r.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM reviews WHERE order_id = $1`, id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM shipments WHERE order_id = $1`, id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM order_items WHERE order_id = $1`, id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := tx.ExecContext(ctx, `DELETE FROM orders WHERE id = $1`, id)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if rows == 0 {
|
||||
_ = tx.Rollback()
|
||||
return errors.New("order not found")
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *Repository) CreateShipment(ctx context.Context, shipment *domain.Shipment) error {
|
||||
now := time.Now().UTC()
|
||||
shipment.CreatedAt = now
|
||||
|
|
|
|||
|
|
@ -64,19 +64,27 @@ func New(cfg config.Config) (*Server, error) {
|
|||
|
||||
mux.Handle("POST /api/companies", chain(http.HandlerFunc(h.CreateCompany), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("GET /api/companies", chain(http.HandlerFunc(h.ListCompanies), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("GET /api/companies/", chain(http.HandlerFunc(h.GetCompany), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("PATCH /api/companies/", chain(http.HandlerFunc(h.UpdateCompany), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("DELETE /api/companies/", chain(http.HandlerFunc(h.DeleteCompany), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("PATCH /api/v1/companies/", chain(http.HandlerFunc(h.VerifyCompany), middleware.Logger, middleware.Gzip, adminOnly))
|
||||
mux.Handle("GET /api/v1/companies/me", chain(http.HandlerFunc(h.GetMyCompany), middleware.Logger, middleware.Gzip, auth))
|
||||
mux.Handle("GET /api/v1/companies/", chain(http.HandlerFunc(h.GetCompanyRating), middleware.Logger, middleware.Gzip))
|
||||
|
||||
mux.Handle("POST /api/products", chain(http.HandlerFunc(h.CreateProduct), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("GET /api/products", chain(http.HandlerFunc(h.ListProducts), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("GET /api/products/", chain(http.HandlerFunc(h.GetProduct), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("PATCH /api/products/", chain(http.HandlerFunc(h.UpdateProduct), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("DELETE /api/products/", chain(http.HandlerFunc(h.DeleteProduct), middleware.Logger, middleware.Gzip))
|
||||
|
||||
mux.Handle("GET /api/v1/inventory", chain(http.HandlerFunc(h.ListInventory), middleware.Logger, middleware.Gzip, auth))
|
||||
mux.Handle("POST /api/v1/inventory/adjust", chain(http.HandlerFunc(h.AdjustInventory), middleware.Logger, middleware.Gzip, auth))
|
||||
|
||||
mux.Handle("POST /api/orders", chain(http.HandlerFunc(h.CreateOrder), middleware.Logger, middleware.Gzip, auth))
|
||||
mux.Handle("GET /api/orders", chain(http.HandlerFunc(h.ListOrders), middleware.Logger, middleware.Gzip, auth))
|
||||
mux.Handle("GET /api/orders/", chain(http.HandlerFunc(h.GetOrder), middleware.Logger, middleware.Gzip, auth))
|
||||
mux.Handle("PATCH /api/orders/", chain(http.HandlerFunc(h.UpdateOrderStatus), middleware.Logger, middleware.Gzip, auth))
|
||||
mux.Handle("DELETE /api/orders/", chain(http.HandlerFunc(h.DeleteOrder), middleware.Logger, middleware.Gzip, auth))
|
||||
mux.Handle("POST /api/orders/", chain(http.HandlerFunc(h.CreatePaymentPreference), middleware.Logger, middleware.Gzip, auth))
|
||||
|
||||
mux.Handle("POST /api/v1/shipments", chain(http.HandlerFunc(h.CreateShipment), middleware.Logger, middleware.Gzip, auth))
|
||||
|
|
|
|||
|
|
@ -20,16 +20,21 @@ type Repository interface {
|
|||
ListCompanies(ctx context.Context) ([]domain.Company, error)
|
||||
GetCompany(ctx context.Context, id uuid.UUID) (*domain.Company, error)
|
||||
UpdateCompany(ctx context.Context, company *domain.Company) error
|
||||
DeleteCompany(ctx context.Context, id uuid.UUID) error
|
||||
|
||||
CreateProduct(ctx context.Context, product *domain.Product) error
|
||||
ListProducts(ctx context.Context) ([]domain.Product, error)
|
||||
GetProduct(ctx context.Context, id uuid.UUID) (*domain.Product, error)
|
||||
UpdateProduct(ctx context.Context, product *domain.Product) error
|
||||
DeleteProduct(ctx context.Context, id uuid.UUID) error
|
||||
AdjustInventory(ctx context.Context, productID uuid.UUID, delta int64, reason string) (*domain.InventoryItem, error)
|
||||
ListInventory(ctx context.Context, filter domain.InventoryFilter) ([]domain.InventoryItem, error)
|
||||
|
||||
CreateOrder(ctx context.Context, order *domain.Order) error
|
||||
ListOrders(ctx context.Context) ([]domain.Order, error)
|
||||
GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error)
|
||||
UpdateOrderStatus(ctx context.Context, id uuid.UUID, status domain.OrderStatus) error
|
||||
DeleteOrder(ctx context.Context, id uuid.UUID) error
|
||||
CreateShipment(ctx context.Context, shipment *domain.Shipment) error
|
||||
GetShipmentByOrderID(ctx context.Context, orderID uuid.UUID) (*domain.Shipment, error)
|
||||
|
||||
|
|
@ -89,6 +94,14 @@ func (s *Service) GetCompany(ctx context.Context, id uuid.UUID) (*domain.Company
|
|||
return s.repo.GetCompany(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateCompany(ctx context.Context, company *domain.Company) error {
|
||||
return s.repo.UpdateCompany(ctx, company)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteCompany(ctx context.Context, id uuid.UUID) error {
|
||||
return s.repo.DeleteCompany(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) RegisterProduct(ctx context.Context, product *domain.Product) error {
|
||||
product.ID = uuid.Must(uuid.NewV7())
|
||||
return s.repo.CreateProduct(ctx, product)
|
||||
|
|
@ -98,6 +111,18 @@ func (s *Service) ListProducts(ctx context.Context) ([]domain.Product, error) {
|
|||
return s.repo.ListProducts(ctx)
|
||||
}
|
||||
|
||||
func (s *Service) GetProduct(ctx context.Context, id uuid.UUID) (*domain.Product, error) {
|
||||
return s.repo.GetProduct(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateProduct(ctx context.Context, product *domain.Product) error {
|
||||
return s.repo.UpdateProduct(ctx, product)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteProduct(ctx context.Context, id uuid.UUID) error {
|
||||
return s.repo.DeleteProduct(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) ListInventory(ctx context.Context, filter domain.InventoryFilter) ([]domain.InventoryItem, error) {
|
||||
return s.repo.ListInventory(ctx, filter)
|
||||
}
|
||||
|
|
@ -112,6 +137,10 @@ func (s *Service) CreateOrder(ctx context.Context, order *domain.Order) error {
|
|||
return s.repo.CreateOrder(ctx, order)
|
||||
}
|
||||
|
||||
func (s *Service) ListOrders(ctx context.Context) ([]domain.Order, error) {
|
||||
return s.repo.ListOrders(ctx)
|
||||
}
|
||||
|
||||
func (s *Service) GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error) {
|
||||
return s.repo.GetOrder(ctx, id)
|
||||
}
|
||||
|
|
@ -120,6 +149,10 @@ func (s *Service) UpdateOrderStatus(ctx context.Context, id uuid.UUID, status do
|
|||
return s.repo.UpdateOrderStatus(ctx, id, status)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteOrder(ctx context.Context, id uuid.UUID) error {
|
||||
return s.repo.DeleteOrder(ctx, id)
|
||||
}
|
||||
|
||||
// CreateShipment persists a freight label for an order if not already present.
|
||||
func (s *Service) CreateShipment(ctx context.Context, shipment *domain.Shipment) error {
|
||||
if _, err := s.repo.GetOrder(ctx, shipment.OrderID); err != nil {
|
||||
|
|
|
|||
Loading…
Reference in a new issue