Add aftersales work order feature

- AftersalesOrder model with state machine (created/pending_confirmation/closed/rejected)
- Public scan-to-confirm flow with phone last-4 verification and rate limiting
- Technician role and middleware for ownership-scoped operations
- QR code generation pointing to /aftersales/{serialNumber}
- Admin overrides: reassign, force-close, delete

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Frudrax Cheng
2026-05-26 10:39:49 +08:00
parent e820b858bf
commit 0d82260fd9
6 changed files with 992 additions and 0 deletions
+85
View File
@@ -209,3 +209,88 @@ type UpdateEmployeeSerialDTO struct {
EmployeeName string `json:"employeeName,omitempty" validate:"omitempty"`
IsActive *bool `json:"isActive,omitempty"`
}
// AftersalesOrder 售后工单模型
type AftersalesOrder struct {
ID uint `gorm:"primaryKey" json:"id"`
SerialNumber string `gorm:"uniqueIndex;size:64" json:"serialNumber"`
CompanyName string `gorm:"index;size:255" json:"companyName"`
CompanyAddress string `gorm:"size:500" json:"companyAddress"`
ContactName string `gorm:"size:100" json:"contactName"`
ContactPhone string `gorm:"size:32" json:"contactPhone"`
ServiceType string `gorm:"size:32" json:"serviceType"`
IssueDescription string `gorm:"type:text" json:"issueDescription"`
ResolutionNote string `gorm:"type:text" json:"resolutionNote"`
WorkOrderStatus string `gorm:"size:32;default:'created'" json:"workOrderStatus"`
AuthorizationStatus string `gorm:"size:32;default:'pending'" json:"authorizationStatus"`
TechnicianID *uint `json:"technicianId"`
CreatedBy *uint `json:"createdBy"`
ScannedAt *time.Time `json:"scannedAt"`
ConfirmedAt *time.Time `json:"confirmedAt"`
RejectCount int `gorm:"default:0" json:"rejectCount"`
CreatedAt time.Time `json:"createdAt"`
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"`
}
// CreateAftersalesOrderDTO 创建售后工单请求数据
type CreateAftersalesOrderDTO struct {
CompanyName string `json:"companyName" validate:"required"`
CompanyAddress string `json:"companyAddress" validate:"required"`
ContactName string `json:"contactName" validate:"required"`
ContactPhone string `json:"contactPhone" validate:"required,len=11"`
ServiceType string `json:"serviceType" validate:"required,oneof=software hardware other"`
IssueDescription string `json:"issueDescription" validate:"required"`
TechnicianID *uint `json:"technicianId,omitempty"`
}
// UpdateAftersalesOrderDTO 更新售后工单请求数据
type UpdateAftersalesOrderDTO struct {
CompanyAddress string `json:"companyAddress,omitempty"`
ContactName string `json:"contactName,omitempty"`
ContactPhone string `json:"contactPhone,omitempty" validate:"omitempty,len=11"`
ServiceType string `json:"serviceType,omitempty" validate:"omitempty,oneof=software hardware other"`
IssueDescription string `json:"issueDescription,omitempty"`
ResolutionNote string `json:"resolutionNote,omitempty"`
TechnicianID *uint `json:"technicianId,omitempty"`
}
// SubmitForConfirmationDTO 提交客户确认请求
type SubmitForConfirmationDTO struct {
ResolutionNote string `json:"resolutionNote" validate:"required"`
}
// CustomerConfirmDTO 客户确认请求
type CustomerConfirmDTO struct {
PhoneLast4 string `json:"phoneLast4" validate:"required,len=4,numeric"`
Action string `json:"action" validate:"required,oneof=authorize reject"`
RejectReason string `json:"rejectReason,omitempty"`
}
// ReassignAftersalesDTO 重新分配技术员请求
type ReassignAftersalesDTO struct {
TechnicianID uint `json:"technicianId" validate:"required"`
}
// AftersalesPublicView 公开查询返回视图(脱敏)
type AftersalesPublicView struct {
SerialNumber string `json:"serialNumber"`
CompanyName string `json:"companyName"`
CompanyAddress string `json:"companyAddress"`
ContactName string `json:"contactName"`
ServiceType string `json:"serviceType"`
IssueDescription string `json:"issueDescription"`
ResolutionNote string `json:"resolutionNote"`
WorkOrderStatus string `json:"workOrderStatus"`
AuthorizationStatus string `json:"authorizationStatus"`
TechnicianName string `json:"technicianName"`
CreatedAt time.Time `json:"createdAt"`
ConfirmedAt *time.Time `json:"confirmedAt"`
}