From d1d189528cf22cda0fe615c56e308db35f6b33b4 Mon Sep 17 00:00:00 2001 From: Frudrax Cheng Date: Thu, 28 May 2026 10:29:17 +0800 Subject: [PATCH] Decouple aftersales customers from company management --- AGENTS.md | 4 +++ README.md | 3 +++ models/models.go | 5 ++-- services/aftersales_service.go | 13 ---------- services/aftersales_service_test.go | 38 ++++++++++++++++++++--------- 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 765524b..4532c0c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -107,6 +107,10 @@ backend-go/ - Creating an employee through `/api/employees` creates employee master data and automatically generates one employee serial bound by `employeeId`. - `admin` / `technician` creation requires an initial password; `employee` creation must not require one. +### Business Boundaries +- Company management (`Company`, `/api/companies`) is for authorized agents/company-code verification. +- Aftersales order `companyName` is customer information stored on the order only; creating or updating an aftersales order must not create or link a `Company` record. + ### Import Organization Standard imports followed by third-party imports, then project imports (sorted alphabetically): ```go diff --git a/README.md b/README.md index 93d3e46..46a4b8d 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,8 @@ go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g main.go ### 企业管理 +企业管理用于维护授权代理商/企业码主体,供客户扫码查询代理商授权状态;它不是售后客户名录。 + | 方法 | 路径 | 描述 | 需要认证 | 角色 | | ------ | ----------------------------- | ------------ | -------- | ------ | | GET | `/api/companies/stats/overview` | 获取企业统计概览 | 是 | 管理员 | @@ -319,6 +321,7 @@ go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g main.go - 服务类型枚举:`software`(软件故障)、`hardware`(硬件故障)、`maintenance`(售后维保) - 工单号格式: `zjbf-sh-YYMMDDNN`(年份后 2 位 + 月份 2 位 + 日期 2 位 + 当天序号至少 2 位,例:`zjbf-sh-26052801`) - 序号按天重置,软删除工单不释放编号(避免回收造成混淆) +- 工单里的企业名称是售后客户信息,只保存在工单中,不会自动创建或关联企业管理记录 - 二维码扫码后客户在网页签名(canvas)后点「已授权」确认;选择「未授权」需填写退回原因 - 签名以 PNG dataURL 形式持久化到工单(`signature` 字段),管理员详情页可查看留底 - 签名校验:必须为 `data:image/png;base64,` 或 `data:image/jpeg;base64,` 前缀,解码后 200B–500KB diff --git a/models/models.go b/models/models.go index 4a09f48..c2d5422 100644 --- a/models/models.go +++ b/models/models.go @@ -277,9 +277,8 @@ type AftersalesOrder struct { UpdatedAt time.Time `json:"updatedAt"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` - Technician *User `gorm:"foreignKey:TechnicianID" json:"technician,omitempty"` - Creator *User `gorm:"foreignKey:CreatedBy" json:"creator,omitempty"` - Company *Company `gorm:"foreignKey:CompanyName;references:CompanyName" json:"company,omitempty"` + Technician *User `gorm:"foreignKey:TechnicianID" json:"technician,omitempty"` + Creator *User `gorm:"foreignKey:CreatedBy" json:"creator,omitempty"` } // CreateAftersalesOrderDTO 创建售后工单请求数据 diff --git a/services/aftersales_service.go b/services/aftersales_service.go index 014453a..a960648 100644 --- a/services/aftersales_service.go +++ b/services/aftersales_service.go @@ -142,19 +142,6 @@ func (s *AftersalesService) generateUniqueSerial() (string, error) { // Create 创建售后工单 func (s *AftersalesService) Create(dto models.CreateAftersalesOrderDTO, userId uint) (*models.AftersalesOrder, error) { - // 确保公司存在 - var company models.Company - result := database.DB.Where("company_name = ?", dto.CompanyName).First(&company) - if result.Error != nil { - company = models.Company{ - CompanyName: dto.CompanyName, - IsActive: true, - } - if err := database.DB.Create(&company).Error; err != nil { - return nil, fmt.Errorf("创建公司失败: %w", err) - } - } - serialNumber, err := s.generateUniqueSerial() if err != nil { return nil, err diff --git a/services/aftersales_service_test.go b/services/aftersales_service_test.go index 4c49813..7cc49ee 100644 --- a/services/aftersales_service_test.go +++ b/services/aftersales_service_test.go @@ -79,7 +79,32 @@ func TestAftersalesService_Create_GeneratesYYMMDDNNSerial(t *testing.T) { assert.Equal(t, user.ID, *order.TechnicianID) database.DB.Unscoped().Delete(order) - database.DB.Unscoped().Where("company_name = ?", "AftersalesSerialCo").Delete(&models.Company{}) +} + +func TestAftersalesService_Create_DoesNotCreateManagedCompany(t *testing.T) { + user := seedTechnician(t, "aftersales_no_company_tech") + defer database.DB.Unscoped().Delete(&user) + + companyName := "AftersalesCustomerOnlyCo" + database.DB.Unscoped().Where("company_name = ?", companyName).Delete(&models.Company{}) + + svc := AftersalesService{} + order, err := svc.Create(models.CreateAftersalesOrderDTO{ + CompanyName: companyName, + CompanyAddress: "客户现场地址", + ContactName: "王五", + ContactPhone: "13800001235", + ServiceType: "software", + IssueDescription: "售后客户问题", + }, user.ID) + + assert.NoError(t, err) + assert.NotNil(t, order) + defer database.DB.Unscoped().Delete(order) + + var count int64 + database.DB.Model(&models.Company{}).Where("company_name = ?", companyName).Count(&count) + assert.Equal(t, int64(0), count) } func TestAftersalesService_Create_SerialIncrementsWithinDay(t *testing.T) { @@ -105,7 +130,6 @@ func TestAftersalesService_Create_SerialIncrementsWithinDay(t *testing.T) { database.DB.Unscoped().Delete(first) database.DB.Unscoped().Delete(second) - database.DB.Unscoped().Where("company_name = ?", "SeqCo").Delete(&models.Company{}) } func TestAftersalesService_SubmitForConfirmation_OwnerOnly(t *testing.T) { @@ -116,7 +140,6 @@ func TestAftersalesService_SubmitForConfirmation_OwnerOnly(t *testing.T) { order := createOrderFor(t, owner, "13800003000") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} @@ -141,7 +164,6 @@ func TestAftersalesService_SubmitForConfirmation_RejectsClosed(t *testing.T) { order := createOrderFor(t, owner, "13800004000") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} @@ -163,7 +185,6 @@ func TestAftersalesService_PublicQuery_MasksPhoneAndSetsScannedAt(t *testing.T) order := createOrderFor(t, owner, "13800005000") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} view, err := svc.PublicQuery(order.SerialNumber) @@ -186,7 +207,6 @@ func TestAftersalesService_CustomerConfirm_Authorize(t *testing.T) { order := createOrderFor(t, owner, "13800006789") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} _, _ = svc.SubmitForConfirmation(order.SerialNumber, models.SubmitForConfirmationDTO{ @@ -217,7 +237,6 @@ func TestAftersalesService_CustomerConfirm_AuthorizeRejectsEmptySignature(t *tes order := createOrderFor(t, owner, "13800007777") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} _, _ = svc.SubmitForConfirmation(order.SerialNumber, models.SubmitForConfirmationDTO{ @@ -239,7 +258,6 @@ func TestAftersalesService_CustomerConfirm_AuthorizeRejectsInvalidSignature(t *t order := createOrderFor(t, owner, "13800007788") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} _, _ = svc.SubmitForConfirmation(order.SerialNumber, models.SubmitForConfirmationDTO{ @@ -271,7 +289,6 @@ func TestAftersalesService_CustomerConfirm_RejectRequiresReason(t *testing.T) { order := createOrderFor(t, owner, "13800008877") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} _, _ = svc.SubmitForConfirmation(order.SerialNumber, models.SubmitForConfirmationDTO{ @@ -293,7 +310,6 @@ func TestAftersalesService_CustomerConfirm_RejectIncrementsCount(t *testing.T) { order := createOrderFor(t, owner, "13800008888") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} _, _ = svc.SubmitForConfirmation(order.SerialNumber, models.SubmitForConfirmationDTO{ @@ -328,7 +344,6 @@ func TestAftersalesService_CustomerConfirm_RejectsWrongStatus(t *testing.T) { order := createOrderFor(t, owner, "13800009999") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} // 未提交客户确认,工单仍是 created,应该拒绝 @@ -345,7 +360,6 @@ func TestAftersalesService_ForceClose_AdminOverride(t *testing.T) { order := createOrderFor(t, owner, "13800001111") defer database.DB.Unscoped().Delete(order) - defer database.DB.Unscoped().Where("company_name = ?", order.CompanyName).Delete(&models.Company{}) svc := AftersalesService{} updated, err := svc.ForceClose(order.SerialNumber)