mirror of
https://github.com/go-admin-team/go-admin.git
synced 2026-09-21 18:20:50 +00:00
No generic CRUD action anywhere: real business - a cross-table order placement, a payment transition - is what the contract surface has to carry, and the actions cover only the single-table case that a real application outgrows immediately. The transaction is Orm.Transaction(), not the Begin/defer shape that app/admin/service/sys_role.go and three other files use. That shape commits a half-written transaction when the body panics, because the deferred check reads err, which a panic leaves nil. The payment transition guards concurrency through the update itself - WHERE status = 'pending' plus RowsAffected - rather than a read followed by a write. The tests cover both rollback paths, because they fail differently: a mid-transaction error returns, a panic unwinds - and the second is what tells Orm.Transaction() apart from the shape it replaces. The concurrency test pins the pool to one writer so sqlite's own single-writer semantics cannot stand in for the guard being tested. The data-scope tests assert the fail-closed direction too: an unrecognised scope must return no rows rather than every row. Claude-Session: https://claude.ai/code/session_01HPTAw8b8tAdFNFn8rKdPYx
58 lines
2.5 KiB
Go
58 lines
2.5 KiB
Go
// Package dto holds app-order's request-binding types.
|
|
//
|
|
// None of them implement core's dto.Index / dto.Control, and none of them
|
|
// define their own Bind method: those interfaces (and the Bind method they
|
|
// require) exist so the framework's generic CRUD Actions
|
|
// (Create/Delete/Index/Update/ViewAction) can bind a request without
|
|
// knowing its concrete type - Action itself calls req.Bind(c). app-order's
|
|
// handlers (apis/order.go) call api.Api.Bind directly on the raw struct
|
|
// instead, exactly as go-admin's own hand-written handlers do (see
|
|
// app/admin/apis/sys_post.go and its service/dto/sys_post.go, which is the
|
|
// same shape: plain structs, no Bind method), so a Bind method here would
|
|
// never be called by anything and would only mislead a reader into thinking
|
|
// it is.
|
|
//
|
|
// What these types do reuse is dto.Pagination (for the list request's page
|
|
// index/size) and the `search` struct-tag convention dto.MakeCondition
|
|
// resolves; both are plain data shapes, not an interface a hand-written
|
|
// handler would otherwise have to reimplement.
|
|
package dto
|
|
|
|
import (
|
|
contractdto "github.com/go-admin-team/go-admin-core/v2/sdk/contract/dto"
|
|
)
|
|
|
|
// OrderItemReq is one line item in a create-order request.
|
|
type OrderItemReq struct {
|
|
ProductName string `json:"productName" validate:"required"`
|
|
Quantity int `json:"quantity" validate:"gte=1"`
|
|
PriceCents int64 `json:"priceCents" validate:"gte=0"`
|
|
}
|
|
|
|
// OrderCreateReq is the create-order request body.
|
|
type OrderCreateReq struct {
|
|
Items []OrderItemReq `json:"items" validate:"required"`
|
|
}
|
|
|
|
// OrderSearchReq is the list-order query.
|
|
//
|
|
// contractdto.MakeCondition reads q's `search` tags through
|
|
// reflect.TypeOf(q).NumField(), which is only valid for a struct Kind - a
|
|
// pointer panics rather than returning an error (see
|
|
// service/order.go:GetPage, which is careful to pass *req, not req). That
|
|
// distinction is not documented on MakeCondition's exported doc comment.
|
|
// Pagination `search:"-"` here follows the same convention the framework's
|
|
// own generic DTOs use to keep Pagination's two fields out of the WHERE
|
|
// clause the tags on Status/OrderNo build.
|
|
type OrderSearchReq struct {
|
|
contractdto.Pagination `search:"-"`
|
|
|
|
Status string `form:"status" search:"type:exact;column:status;table:app_order"`
|
|
OrderNo string `form:"orderNo" search:"type:exact;column:order_no;table:app_order"`
|
|
}
|
|
|
|
// OrderIdReq binds a single :id, for a detail lookup or a Pay request.
|
|
type OrderIdReq struct {
|
|
Id int `uri:"id" validate:"required"`
|
|
}
|