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
+22
View File
@@ -73,4 +73,26 @@ func SetupAPIRoutes(r *gin.RouterGroup) {
employeeSerialsRoutes.POST("/:serialNumber/revoke", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Revoke)
employeeSerialsRoutes.DELETE("/:serialNumber", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), employeeSerialsController.Delete)
}
// 售后工单路由
aftersalesController := controllers.NewAftersalesController()
aftersalesRoutes := r.Group("/aftersales")
{
// 公开(无需登录)
aftersalesRoutes.GET("/:serialNumber/query", aftersalesController.PublicQuery)
aftersalesRoutes.POST("/:serialNumber/confirm", aftersalesController.CustomerConfirm)
// 技术员 + 管理员
aftersalesRoutes.POST("", middleware.JWTAuthMiddleware(), middleware.TechnicianMiddleware(), aftersalesController.Create)
aftersalesRoutes.GET("", middleware.JWTAuthMiddleware(), middleware.TechnicianMiddleware(), aftersalesController.FindAll)
aftersalesRoutes.GET("/:serialNumber", middleware.JWTAuthMiddleware(), middleware.TechnicianMiddleware(), aftersalesController.FindOne)
aftersalesRoutes.PATCH("/:serialNumber", middleware.JWTAuthMiddleware(), middleware.TechnicianMiddleware(), aftersalesController.Update)
aftersalesRoutes.POST("/:serialNumber/qrcode", middleware.JWTAuthMiddleware(), middleware.TechnicianMiddleware(), aftersalesController.GenerateQRCode)
aftersalesRoutes.POST("/:serialNumber/submit", middleware.JWTAuthMiddleware(), middleware.TechnicianMiddleware(), aftersalesController.SubmitForConfirmation)
// 仅管理员
aftersalesRoutes.POST("/:serialNumber/reassign", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), aftersalesController.Reassign)
aftersalesRoutes.POST("/:serialNumber/force-close", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), aftersalesController.ForceClose)
aftersalesRoutes.DELETE("/:serialNumber", middleware.JWTAuthMiddleware(), middleware.AdminMiddleware(), aftersalesController.Delete)
}
}