完善配送订单创建联动
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// 功能描述:实现配送点范围内的合同、合同气瓶和配送订单接口。
|
||||
// 版本:v1.1.0。
|
||||
// 版本:v1.2.0。
|
||||
package delivery
|
||||
|
||||
import (
|
||||
@@ -49,23 +49,33 @@ func scopedContract(ctx *gin.Context, identity string, pointID uint64) (models.G
|
||||
return contract, true
|
||||
}
|
||||
|
||||
// deliveryContractListQuery 构造当前配送点合同列表;订单候选模式额外限定可履约状态。
|
||||
func deliveryContractListQuery(database *gorm.DB, gasID, pointID uint64, candidate string, now time.Time) *gorm.DB {
|
||||
query := platformgasorder.ContractPartyDisplayQuery(database).
|
||||
Where("gasorder_contract.gas_basic_id = ? AND gasorder_contract.delivery_basic_id = ?", gasID, pointID)
|
||||
if candidate == "order" {
|
||||
return platformgasorder.FilterGasorderContractCandidates(query, "order", now)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func ListContract(ctx *gin.Context) {
|
||||
point, _, ok := currentScope(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
page, size := common.PageSize(ctx)
|
||||
query := common.ApplyKeywordFilter(ctx,
|
||||
common.ActiveRecords(db().Model(&models.GasorderContract{})).
|
||||
Where("gas_basic_id = ? AND delivery_basic_id = ?", point.GasBasicID, point.ID),
|
||||
&models.GasorderContract{})
|
||||
query := deliveryContractListQuery(
|
||||
db(), point.GasBasicID, point.ID, strings.TrimSpace(ctx.Query("candidate")), time.Now(),
|
||||
)
|
||||
query = common.ApplyKeywordFilter(ctx, query, &models.GasorderContract{})
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
var list []models.GasorderContract
|
||||
if err := query.Order("gasorder_contract.created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil {
|
||||
var list []platformgasorder.ContractPartyDisplay
|
||||
if err := query.Order("gasorder_contract.created_at desc").Offset((page - 1) * size).Limit(size).Scan(&list).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
@@ -74,7 +84,11 @@ func ListContract(ctx *gin.Context) {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
protected, err := protectDeliveryContractListResponse(ctx, response, list)
|
||||
contracts := make([]models.GasorderContract, 0, len(list))
|
||||
for _, item := range list {
|
||||
contracts = append(contracts, item.GasorderContract)
|
||||
}
|
||||
protected, err := protectDeliveryContractListResponse(ctx, response, contracts)
|
||||
if err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
@@ -186,15 +200,63 @@ func TerminateContract(ctx *gin.Context) {
|
||||
withContract(ctx, platformgasorder.TerminateGasorderContract)
|
||||
}
|
||||
|
||||
// deliveryContractProductQuery 构造当前配送点合同气瓶查询;指定合同时仅保留未解绑候选。
|
||||
func deliveryContractProductQuery(database *gorm.DB, pointID, contractID uint64, keyword string) *gorm.DB {
|
||||
query := common.ActiveRecords(database.Model(&models.GasorderContractProduct{})).
|
||||
Select("gasorder_contract_product.*, COALESCE(display_product.name, '') AS product_name").
|
||||
Joins("JOIN gasorder_contract ON gasorder_contract.id = gasorder_contract_product.gasorder_contract_id").
|
||||
Joins("LEFT JOIN product_info AS display_product ON display_product.id = gasorder_contract_product.product_info_id AND display_product.deleted_at IS NULL AND display_product.status <> ?", common.StatusArchived).
|
||||
Where("gasorder_contract.delivery_basic_id = ?", pointID)
|
||||
if contractID > 0 {
|
||||
query = query.Where("gasorder_contract_product.gasorder_contract_id = ? AND gasorder_contract_product.unbound_at IS NULL", contractID)
|
||||
}
|
||||
keyword = strings.ToLower(strings.TrimSpace(keyword))
|
||||
if keyword == "" {
|
||||
return query
|
||||
}
|
||||
pattern := "%" + keyword + "%"
|
||||
return query.Where("(LOWER(COALESCE(display_product.name, '')) LIKE ? OR LOWER(gasorder_contract_product.product_code) LIKE ? OR LOWER(gasorder_contract_product.product_type_name) LIKE ?)", pattern, pattern, pattern)
|
||||
}
|
||||
|
||||
func ListContractProduct(ctx *gin.Context) {
|
||||
point, _, ok := currentScope(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
query := common.ActiveRecords(db().Model(&models.GasorderContractProduct{})).
|
||||
Joins("JOIN gasorder_contract ON gasorder_contract.id = gasorder_contract_product.gasorder_contract_id").
|
||||
Where("gasorder_contract.delivery_basic_id = ?", point.ID)
|
||||
listScoped(ctx, &models.GasorderContractProduct{}, query, "gasorder_contract_product.created_at desc")
|
||||
contractIdentity := strings.TrimSpace(ctx.Query("contract_identity"))
|
||||
var contractID uint64
|
||||
if contractIdentity != "" {
|
||||
contract, valid := scopedContract(ctx, contractIdentity, point.ID)
|
||||
if !valid {
|
||||
return
|
||||
}
|
||||
contractID = contract.ID
|
||||
}
|
||||
query := deliveryContractProductQuery(db(), point.ID, contractID, ctx.Query("keyword"))
|
||||
page, size := common.PageSize(ctx)
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
type contractProductDisplay struct {
|
||||
models.GasorderContractProduct
|
||||
ProductName string `gorm:"column:product_name" json:"product_name"`
|
||||
}
|
||||
var list []contractProductDisplay
|
||||
if err := query.Order("gasorder_contract_product.created_at desc").Offset((page - 1) * size).Limit(size).Scan(&list).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
response, err := common.PublicResourceResponse(list)
|
||||
if err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
infra.Response.Success(ctx, gin.H{
|
||||
"total": total,
|
||||
"list": common.ProtectPreciseLocation(ctx, &models.GasorderContractProduct{}, response),
|
||||
})
|
||||
}
|
||||
|
||||
func GetContractProduct(ctx *gin.Context) {
|
||||
|
||||
78
backend/api/internal/logic/delivery/order_candidate_test.go
Normal file
78
backend/api/internal/logic/delivery/order_candidate_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// 功能描述:验证配送点创建订单的合同、地址和合同气瓶候选查询范围。
|
||||
// 版本:v1.0.0。
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// candidateTestDatabase 创建只用于生成 SQL 的模拟数据库。
|
||||
func candidateTestDatabase(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
sqlDatabase, _, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("创建模拟数据库失败:%v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = sqlDatabase.Close() })
|
||||
database, err := gorm.Open(postgres.New(postgres.Config{Conn: sqlDatabase}), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("创建 GORM 数据库失败:%v", err)
|
||||
}
|
||||
return database
|
||||
}
|
||||
|
||||
// assertSQLFragments 验证候选查询包含全部范围约束。
|
||||
func assertSQLFragments(t *testing.T, statement string, fragments ...string) {
|
||||
t.Helper()
|
||||
for _, fragment := range fragments {
|
||||
if !strings.Contains(statement, fragment) {
|
||||
t.Fatalf("候选 SQL 缺少 %q:%s", fragment, statement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeliveryOrderContractCandidates(t *testing.T) {
|
||||
database := candidateTestDatabase(t)
|
||||
statement := database.ToSQL(func(tx *gorm.DB) *gorm.DB {
|
||||
return deliveryContractListQuery(tx, 7, 9, "order", time.Now()).Find(&[]map[string]any{})
|
||||
})
|
||||
assertSQLFragments(t, statement,
|
||||
"gasorder_contract.gas_basic_id = 7",
|
||||
"gasorder_contract.delivery_basic_id = 9",
|
||||
"candidate_relation.user_account_id = gasorder_contract.user_account_id",
|
||||
"contract_status = 11",
|
||||
)
|
||||
}
|
||||
|
||||
func TestDeliveryOrderAddressCandidates(t *testing.T) {
|
||||
database := candidateTestDatabase(t)
|
||||
statement := database.ToSQL(func(tx *gorm.DB) *gorm.DB {
|
||||
return addressQueryForContract(
|
||||
addressQueryWithDatabase(tx, 9), 31,
|
||||
).Find(&[]map[string]any{})
|
||||
})
|
||||
assertSQLFragments(t, statement,
|
||||
"user_service_relation.delivery_basic_id = 9",
|
||||
"user_address.user_account_id = 31",
|
||||
)
|
||||
}
|
||||
|
||||
func TestDeliveryOrderContractProductCandidates(t *testing.T) {
|
||||
database := candidateTestDatabase(t)
|
||||
statement := database.ToSQL(func(tx *gorm.DB) *gorm.DB {
|
||||
return deliveryContractProductQuery(tx, 9, 27, "阀门").Find(&[]map[string]any{})
|
||||
})
|
||||
assertSQLFragments(t, statement,
|
||||
"gasorder_contract.delivery_basic_id = 9",
|
||||
"gasorder_contract_product.gasorder_contract_id = 27",
|
||||
"gasorder_contract_product.unbound_at IS NULL",
|
||||
"display_product.name",
|
||||
"gasorder_contract_product.product_code",
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
// 功能描述:实现配送点范围内的用户、服务关系和收货地址管理。
|
||||
// 版本:v1.1.0。
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/infra"
|
||||
@@ -231,16 +234,35 @@ func ArchiveUser(ctx *gin.Context) {
|
||||
}
|
||||
|
||||
func addressQuery(pointID uint64) *gorm.DB {
|
||||
return common.ActiveRecords(db().Model(&models.UserAddress{})).
|
||||
return addressQueryWithDatabase(db(), pointID)
|
||||
}
|
||||
|
||||
// addressQueryWithDatabase 构造配送点有效服务用户的地址范围,便于独立验证查询约束。
|
||||
func addressQueryWithDatabase(database *gorm.DB, pointID uint64) *gorm.DB {
|
||||
return common.ActiveRecords(database.Model(&models.UserAddress{})).
|
||||
Joins("JOIN user_service_relation ON user_service_relation.user_account_id = user_address.user_account_id AND user_service_relation.status <> ?",
|
||||
common.StatusArchived).Where("user_service_relation.delivery_basic_id = ?", pointID)
|
||||
}
|
||||
|
||||
// addressQueryForContract 将当前配送点地址进一步限定为合同签约用户。
|
||||
func addressQueryForContract(query *gorm.DB, userAccountID uint64) *gorm.DB {
|
||||
return query.Where("user_address.user_account_id = ?", userAccountID)
|
||||
}
|
||||
|
||||
func ListAddress(ctx *gin.Context) {
|
||||
point, _, ok := currentScope(ctx)
|
||||
if ok {
|
||||
listScoped(ctx, &models.UserAddress{}, addressQuery(point.ID), "user_address.created_at desc")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
query := addressQuery(point.ID)
|
||||
if contractIdentity := strings.TrimSpace(ctx.Query("gasorder_contract_identity")); contractIdentity != "" {
|
||||
contract, valid := scopedContract(ctx, contractIdentity, point.ID)
|
||||
if !valid {
|
||||
return
|
||||
}
|
||||
query = addressQueryForContract(query, contract.UserAccountID)
|
||||
}
|
||||
listScoped(ctx, &models.UserAddress{}, query, "user_address.is_default desc, user_address.created_at desc")
|
||||
}
|
||||
|
||||
func GetAddress(ctx *gin.Context) {
|
||||
|
||||
Reference in New Issue
Block a user