Decouple aftersales customers from company management
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-3
@@ -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 创建售后工单请求数据
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user