完善合同气瓶绑定约束与候选过滤

- 明确合同状态、气瓶归属、停用、报废和重复绑定失败原因
- 根据当前合同过滤可绑定气瓶,排除无效及重复候选项
- 从合同列表进入绑定页时锁定预填合同,防止切换上下文
- 切换通用新建页合同后清空旧气瓶并重新加载候选项
- 增加前后端回归检查并同步中文操作日志
This commit is contained in:
czl231
2026-08-13 00:10:42 +08:00
parent 96d37d63cf
commit 9937e3a508
11 changed files with 120 additions and 10 deletions

View File

@@ -27,6 +27,22 @@ func TestContractChangeBusinessErrors(t *testing.T) {
}
}
// TestContractBindingErrorMessages 验证绑定失败时给出可操作的中文原因。
func TestContractBindingErrorMessages(t *testing.T) {
want := map[error]string{
errBindingContractStatus: "仅草稿合同可以绑定气瓶",
errBindingProductDisabled: "所选气瓶已停用,不能绑定到合同",
errBindingProductScrapped: "所选气瓶已报废,不能绑定到合同",
errBindingProductOwner: "所选气瓶不属于该合同用户,请选择合同用户所属的气瓶",
errBindingProductDuplicate: "所选气瓶已绑定到该合同,请勿重复绑定",
}
for businessErr, message := range want {
if businessErr.Error() != message {
t.Fatalf("绑定错误提示不清晰:得到 %q期望 %q", businessErr.Error(), message)
}
}
}
// TestContractActivationErrorMessages 验证关键启用失败原因保持清晰中文。
func TestContractActivationErrorMessages(t *testing.T) {
want := map[error]string{

View File

@@ -29,6 +29,11 @@ var (
errContractNotEffective = errors.New("合同尚未到生效时间,暂时不能启用")
errContractExpired = errors.New("合同已到期,请调整到期时间或续签后再启用")
errContractNoProduct = errors.New("合同尚未绑定有效气瓶,请先绑定气瓶后再启用")
errBindingContractStatus = errors.New("仅草稿合同可以绑定气瓶")
errBindingProductDisabled = errors.New("所选气瓶已停用,不能绑定到合同")
errBindingProductScrapped = errors.New("所选气瓶已报废,不能绑定到合同")
errBindingProductOwner = errors.New("所选气瓶不属于该合同用户,请选择合同用户所属的气瓶")
errBindingProductDuplicate = errors.New("所选气瓶已绑定到该合同,请勿重复绑定")
)
func ListGasorderContract(ctx *gin.Context) { common.ListResource(ctx, &models.GasorderContract{}) }
@@ -424,14 +429,36 @@ func BindGasorderContractProduct(ctx *gin.Context) {
common.RespondRecordError(ctx, err)
return
}
if contract.ContractStatus != common.StatusDraft && contract.ContractStatus != common.StatusActive {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
if contract.ContractStatus != common.StatusDraft {
infra.Response.Error(ctx, errBindingContractStatus)
return
}
var product models.ProductInfo
if err := impl.DBService.Where("identity = ?", request.ProductIdentity).First(&product).Error; err != nil ||
product.Status != common.StatusEnable || product.ProductStatus == common.StatusScrapped || product.UserAccountID != contract.UserAccountID {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
if err := impl.DBService.Where("identity = ?", request.ProductIdentity).First(&product).Error; err != nil {
common.RespondRecordError(ctx, err)
return
}
if product.Status != common.StatusEnable {
infra.Response.Error(ctx, errBindingProductDisabled)
return
}
if product.ProductStatus == common.StatusScrapped {
infra.Response.Error(ctx, errBindingProductScrapped)
return
}
if product.UserAccountID != contract.UserAccountID {
infra.Response.Error(ctx, errBindingProductOwner)
return
}
var activeBindingCount int64
if err := impl.DBService.Model(&models.GasorderContractProduct{}).
Where("gasorder_contract_id = ? AND product_info_id = ? AND unbound_at IS NULL", contract.ID, product.ID).
Count(&activeBindingCount).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
if activeBindingCount > 0 {
infra.Response.Error(ctx, errBindingProductDuplicate)
return
}
var productType models.ProductType