|
|
@@ -0,0 +1,646 @@
|
|
|
+# Web开发特训章节
|
|
|
+
|
|
|
+## 学习目标
|
|
|
+- 掌握Go语言Web开发的核心技术栈
|
|
|
+- 理解现代Web应用架构设计
|
|
|
+- 能够独立开发生产级别的Web应用
|
|
|
+- 掌握前后端分离开发模式
|
|
|
+
|
|
|
+## 特训内容概览
|
|
|
+
|
|
|
+### 5.1 Web框架深度掌握 (2周)
|
|
|
+### 5.2 前后端分离架构 (2周)
|
|
|
+### 5.3 高级Web特性 (2周)
|
|
|
+### 5.4 性能优化与安全 (1周)
|
|
|
+### 5.5 实战项目:电商平台 (3周)
|
|
|
+
|
|
|
+## 5.1 Web框架深度掌握
|
|
|
+
|
|
|
+### 5.1.1 Gin框架核心特性
|
|
|
+```go
|
|
|
+// 路由分组与中间件
|
|
|
+func setupRouter() *gin.Engine {
|
|
|
+ r := gin.Default()
|
|
|
+
|
|
|
+ // 全局中间件
|
|
|
+ r.Use(gin.Logger())
|
|
|
+ r.Use(gin.Recovery())
|
|
|
+
|
|
|
+ // API路由组
|
|
|
+ api := r.Group("/api/v1")
|
|
|
+ {
|
|
|
+ // 用户相关路由
|
|
|
+ users := api.Group("/users")
|
|
|
+ {
|
|
|
+ users.GET("", userHandler.ListUsers)
|
|
|
+ users.POST("", userHandler.CreateUser)
|
|
|
+ users.GET("/:id", userHandler.GetUser)
|
|
|
+ users.PUT("/:id", userHandler.UpdateUser)
|
|
|
+ users.DELETE("/:id", userHandler.DeleteUser)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 商品相关路由
|
|
|
+ products := api.Group("/products")
|
|
|
+ {
|
|
|
+ products.GET("", productHandler.ListProducts)
|
|
|
+ products.POST("", productHandler.CreateProduct)
|
|
|
+ products.GET("/:id", productHandler.GetProduct)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return r
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.1.2 自定义中间件开发
|
|
|
+```go
|
|
|
+// 日志中间件
|
|
|
+func LoggerMiddleware() gin.HandlerFunc {
|
|
|
+ return func(c *gin.Context) {
|
|
|
+ start := time.Now()
|
|
|
+
|
|
|
+ c.Next()
|
|
|
+
|
|
|
+ latency := time.Since(start)
|
|
|
+ status := c.Writer.Status()
|
|
|
+ method := c.Request.Method
|
|
|
+ path := c.Request.URL.Path
|
|
|
+
|
|
|
+ log.Printf("[%s] %s %d %s", method, path, status, latency)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 限流中间件
|
|
|
+func RateLimitMiddleware(limit int) gin.HandlerFunc {
|
|
|
+ limiter := rate.NewLimiter(rate.Limit(limit), limit)
|
|
|
+
|
|
|
+ return func(c *gin.Context) {
|
|
|
+ if !limiter.Allow() {
|
|
|
+ c.JSON(429, gin.H{"error": "请求过于频繁"})
|
|
|
+ c.Abort()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ c.Next()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// CORS中间件
|
|
|
+func CORSMiddleware() gin.HandlerFunc {
|
|
|
+ return func(c *gin.Context) {
|
|
|
+ c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
|
|
+ c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
|
|
+ c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
|
|
+
|
|
|
+ if c.Request.Method == "OPTIONS" {
|
|
|
+ c.AbortWithStatus(204)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ c.Next()
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.1.3 请求处理与数据绑定
|
|
|
+```go
|
|
|
+// 复杂数据绑定
|
|
|
+type CreateProductRequest struct {
|
|
|
+ Name string `json:"name" binding:"required,min=2,max=100"`
|
|
|
+ Description string `json:"description" binding:"max=500"`
|
|
|
+ Price float64 `json:"price" binding:"required,min=0"`
|
|
|
+ Stock int `json:"stock" binding:"min=0"`
|
|
|
+ CategoryID uint `json:"category_id" binding:"required"`
|
|
|
+ Images []string `json:"images"`
|
|
|
+}
|
|
|
+
|
|
|
+// 文件上传处理
|
|
|
+func UploadHandler(c *gin.Context) {
|
|
|
+ file, err := c.FormFile("file")
|
|
|
+ if err != nil {
|
|
|
+ c.JSON(400, gin.H{"error": "文件上传失败"})
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证文件类型
|
|
|
+ allowedTypes := map[string]bool{
|
|
|
+ "image/jpeg": true,
|
|
|
+ "image/png": true,
|
|
|
+ "image/gif": true,
|
|
|
+ }
|
|
|
+
|
|
|
+ if !allowedTypes[file.Header.Get("Content-Type")] {
|
|
|
+ c.JSON(400, gin.H{"error": "不支持的文件类型"})
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存文件
|
|
|
+ filename := fmt.Sprintf("uploads/%s", file.Filename)
|
|
|
+ if err := c.SaveUploadedFile(file, filename); err != nil {
|
|
|
+ c.JSON(500, gin.H{"error": "文件保存失败"})
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ c.JSON(200, gin.H{"message": "上传成功", "filename": filename})
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 5.2 前后端分离架构
|
|
|
+
|
|
|
+### 5.2.1 RESTful API设计规范
|
|
|
+```go
|
|
|
+// 统一响应格式
|
|
|
+type APIResponse struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Message string `json:"message"`
|
|
|
+ Data interface{} `json:"data,omitempty"`
|
|
|
+ Meta *MetaInfo `json:"meta,omitempty"`
|
|
|
+}
|
|
|
+
|
|
|
+type MetaInfo struct {
|
|
|
+ Page int `json:"page"`
|
|
|
+ PageSize int `json:"page_size"`
|
|
|
+ Total int `json:"total"`
|
|
|
+ TotalPage int `json:"total_page"`
|
|
|
+}
|
|
|
+
|
|
|
+// 成功响应
|
|
|
+func SuccessResponse(data interface{}) APIResponse {
|
|
|
+ return APIResponse{
|
|
|
+ Code: 200,
|
|
|
+ Message: "success",
|
|
|
+ Data: data,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 分页响应
|
|
|
+func PaginatedResponse(data interface{}, page, pageSize, total int) APIResponse {
|
|
|
+ totalPage := (total + pageSize - 1) / pageSize
|
|
|
+
|
|
|
+ return APIResponse{
|
|
|
+ Code: 200,
|
|
|
+ Message: "success",
|
|
|
+ Data: data,
|
|
|
+ Meta: &MetaInfo{
|
|
|
+ Page: page,
|
|
|
+ PageSize: pageSize,
|
|
|
+ Total: total,
|
|
|
+ TotalPage: totalPage,
|
|
|
+ },
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.2.2 JWT认证与权限控制
|
|
|
+```go
|
|
|
+// JWT服务
|
|
|
+type JWTService struct {
|
|
|
+ secretKey []byte
|
|
|
+}
|
|
|
+
|
|
|
+func NewJWTService(secret string) *JWTService {
|
|
|
+ return &JWTService{
|
|
|
+ secretKey: []byte(secret),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (j *JWTService) GenerateToken(userID uint, roles []string) (string, error) {
|
|
|
+ claims := jwt.MapClaims{
|
|
|
+ "user_id": userID,
|
|
|
+ "roles": roles,
|
|
|
+ "exp": time.Now().Add(24 * time.Hour).Unix(),
|
|
|
+ }
|
|
|
+
|
|
|
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
|
+ return token.SignedString(j.secretKey)
|
|
|
+}
|
|
|
+
|
|
|
+func (j *JWTService) ValidateToken(tokenString string) (jwt.MapClaims, error) {
|
|
|
+ token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
|
|
+ if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
|
+ return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
|
|
+ }
|
|
|
+ return j.secretKey, nil
|
|
|
+ })
|
|
|
+
|
|
|
+ if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
|
|
+ return claims, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil, err
|
|
|
+}
|
|
|
+
|
|
|
+// 基于角色的权限控制
|
|
|
+func RoleBasedAuth(requiredRoles ...string) gin.HandlerFunc {
|
|
|
+ return func(c *gin.Context) {
|
|
|
+ claims, exists := c.Get("claims")
|
|
|
+ if !exists {
|
|
|
+ c.JSON(401, gin.H{"error": "未授权"})
|
|
|
+ c.Abort()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ jwtClaims := claims.(jwt.MapClaims)
|
|
|
+ userRoles, ok := jwtClaims["roles"].([]interface{})
|
|
|
+ if !ok {
|
|
|
+ c.JSON(403, gin.H{"error": "权限不足"})
|
|
|
+ c.Abort()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查用户角色是否包含所需角色
|
|
|
+ hasPermission := false
|
|
|
+ for _, requiredRole := range requiredRoles {
|
|
|
+ for _, userRole := range userRoles {
|
|
|
+ if userRole == requiredRole {
|
|
|
+ hasPermission = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if !hasPermission {
|
|
|
+ c.JSON(403, gin.H{"error": "权限不足"})
|
|
|
+ c.Abort()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ c.Next()
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.2.3 WebSocket实时通信
|
|
|
+```go
|
|
|
+// WebSocket连接管理
|
|
|
+type ConnectionManager struct {
|
|
|
+ connections map[*websocket.Conn]bool
|
|
|
+ broadcast chan []byte
|
|
|
+ register chan *websocket.Conn
|
|
|
+ unregister chan *websocket.Conn
|
|
|
+ mutex sync.RWMutex
|
|
|
+}
|
|
|
+
|
|
|
+func NewConnectionManager() *ConnectionManager {
|
|
|
+ return &ConnectionManager{
|
|
|
+ connections: make(map[*websocket.Conn]bool),
|
|
|
+ broadcast: make(chan []byte),
|
|
|
+ register: make(chan *websocket.Conn),
|
|
|
+ unregister: make(chan *websocket.Conn),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (cm *ConnectionManager) Run() {
|
|
|
+ for {
|
|
|
+ select {
|
|
|
+ case conn := <-cm.register:
|
|
|
+ cm.mutex.Lock()
|
|
|
+ cm.connections[conn] = true
|
|
|
+ cm.mutex.Unlock()
|
|
|
+
|
|
|
+ case conn := <-cm.unregister:
|
|
|
+ cm.mutex.Lock()
|
|
|
+ if _, ok := cm.connections[conn]; ok {
|
|
|
+ delete(cm.connections, conn)
|
|
|
+ conn.Close()
|
|
|
+ }
|
|
|
+ cm.mutex.Unlock()
|
|
|
+
|
|
|
+ case message := <-cm.broadcast:
|
|
|
+ cm.mutex.RLock()
|
|
|
+ for conn := range cm.connections {
|
|
|
+ if err := conn.WriteMessage(websocket.TextMessage, message); err != nil {
|
|
|
+ cm.unregister <- conn
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cm.mutex.RUnlock()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// WebSocket处理器
|
|
|
+func WebSocketHandler(cm *ConnectionManager) gin.HandlerFunc {
|
|
|
+ var upgrader = websocket.Upgrader{
|
|
|
+ CheckOrigin: func(r *http.Request) bool {
|
|
|
+ return true
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ return func(c *gin.Context) {
|
|
|
+ conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
|
|
+ if err != nil {
|
|
|
+ log.Printf("WebSocket升级失败: %v", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ cm.register <- conn
|
|
|
+
|
|
|
+ defer func() {
|
|
|
+ cm.unregister <- conn
|
|
|
+ }()
|
|
|
+
|
|
|
+ for {
|
|
|
+ messageType, message, err := conn.ReadMessage()
|
|
|
+ if err != nil {
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理接收到的消息
|
|
|
+ if messageType == websocket.TextMessage {
|
|
|
+ cm.broadcast <- message
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 5.3 高级Web特性
|
|
|
+
|
|
|
+### 5.3.1 模板引擎与静态文件服务
|
|
|
+```go
|
|
|
+// HTML模板渲染
|
|
|
+func setupTemplateEngine() *gin.Engine {
|
|
|
+ r := gin.Default()
|
|
|
+
|
|
|
+ // 加载模板
|
|
|
+ r.LoadHTMLGlob("templates/*")
|
|
|
+
|
|
|
+ // 静态文件服务
|
|
|
+ r.Static("/static", "./static")
|
|
|
+ r.StaticFile("/favicon.ico", "./static/favicon.ico")
|
|
|
+
|
|
|
+ return r
|
|
|
+}
|
|
|
+
|
|
|
+// 模板处理器
|
|
|
+func HomeHandler(c *gin.Context) {
|
|
|
+ c.HTML(200, "index.html", gin.H{
|
|
|
+ "title": "首页",
|
|
|
+ "user": gin.H{
|
|
|
+ "name": "张三",
|
|
|
+ "email": "[email protected]",
|
|
|
+ },
|
|
|
+ })
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.3.2 文件上传与云存储集成
|
|
|
+```go
|
|
|
+// 多文件上传
|
|
|
+type UploadService struct {
|
|
|
+ cloudStorage CloudStorage
|
|
|
+}
|
|
|
+
|
|
|
+func (us *UploadService) UploadMultipleFiles(files []*multipart.FileHeader) ([]string, error) {
|
|
|
+ var uploadedFiles []string
|
|
|
+
|
|
|
+ for _, file := range files {
|
|
|
+ filename, err := us.uploadSingleFile(file)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ uploadedFiles = append(uploadedFiles, filename)
|
|
|
+ }
|
|
|
+
|
|
|
+ return uploadedFiles, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (us *UploadService) uploadSingleFile(file *multipart.FileHeader) (string, error) {
|
|
|
+ // 打开文件
|
|
|
+ src, err := file.Open()
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ defer src.Close()
|
|
|
+
|
|
|
+ // 生成唯一文件名
|
|
|
+ ext := filepath.Ext(file.Filename)
|
|
|
+ filename := fmt.Sprintf("%s%s", uuid.New().String(), ext)
|
|
|
+
|
|
|
+ // 上传到云存储
|
|
|
+ if err := us.cloudStorage.Upload(filename, src); err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ return filename, nil
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.3.3 缓存与Session管理
|
|
|
+```go
|
|
|
+// Redis缓存服务
|
|
|
+type CacheService struct {
|
|
|
+ client *redis.Client
|
|
|
+}
|
|
|
+
|
|
|
+func NewCacheService(addr, password string) *CacheService {
|
|
|
+ client := redis.NewClient(&redis.Options{
|
|
|
+ Addr: addr,
|
|
|
+ Password: password,
|
|
|
+ DB: 0,
|
|
|
+ })
|
|
|
+
|
|
|
+ return &CacheService{client: client}
|
|
|
+}
|
|
|
+
|
|
|
+func (cs *CacheService) Set(key string, value interface{}, expiration time.Duration) error {
|
|
|
+ return cs.client.Set(context.Background(), key, value, expiration).Err()
|
|
|
+}
|
|
|
+
|
|
|
+func (cs *CacheService) Get(key string) (string, error) {
|
|
|
+ return cs.client.Get(context.Background(), key).Result()
|
|
|
+}
|
|
|
+
|
|
|
+func (cs *CacheService) Delete(key string) error {
|
|
|
+ return cs.client.Del(context.Background(), key).Err()
|
|
|
+}
|
|
|
+
|
|
|
+// Session管理
|
|
|
+func SessionMiddleware(store sessions.Store) gin.HandlerFunc {
|
|
|
+ return func(c *gin.Context) {
|
|
|
+ session, _ := store.Get(c.Request, "session-name")
|
|
|
+ c.Set("session", session)
|
|
|
+
|
|
|
+ c.Next()
|
|
|
+
|
|
|
+ session.Save(c.Request, c.Writer)
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 5.4 性能优化与安全
|
|
|
+
|
|
|
+### 5.4.1 性能优化技巧
|
|
|
+```go
|
|
|
+// 数据库连接池优化
|
|
|
+func setupDatabase() *sql.DB {
|
|
|
+ db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname")
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 连接池配置
|
|
|
+ db.SetMaxOpenConns(25)
|
|
|
+ db.SetMaxIdleConns(25)
|
|
|
+ db.SetConnMaxLifetime(5 * time.Minute)
|
|
|
+
|
|
|
+ return db
|
|
|
+}
|
|
|
+
|
|
|
+// 响应压缩
|
|
|
+func CompressionMiddleware() gin.HandlerFunc {
|
|
|
+ return gin.Gzip(gin.DefaultCompression)
|
|
|
+}
|
|
|
+
|
|
|
+// 静态资源缓存
|
|
|
+func StaticCacheMiddleware() gin.HandlerFunc {
|
|
|
+ return func(c *gin.Context) {
|
|
|
+ if strings.HasPrefix(c.Request.URL.Path, "/static/") {
|
|
|
+ c.Header("Cache-Control", "public, max-age=31536000")
|
|
|
+ }
|
|
|
+ c.Next()
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.4.2 安全防护措施
|
|
|
+```go
|
|
|
+// SQL注入防护
|
|
|
+func SafeQuery(db *sql.DB, query string, args ...interface{}) (*sql.Rows, error) {
|
|
|
+ // 使用参数化查询
|
|
|
+ return db.Query(query, args...)
|
|
|
+}
|
|
|
+
|
|
|
+// XSS防护
|
|
|
+func XSSMiddleware() gin.HandlerFunc {
|
|
|
+ return func(c *gin.Context) {
|
|
|
+ c.Header("X-XSS-Protection", "1; mode=block")
|
|
|
+ c.Header("X-Content-Type-Options", "nosniff")
|
|
|
+ c.Next()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// CSRF防护
|
|
|
+func CSRFMiddleware(secret string) gin.HandlerFunc {
|
|
|
+ return csrf.Middleware(csrf.Options{
|
|
|
+ Secret: secret,
|
|
|
+ ErrorFunc: func(c *gin.Context) {
|
|
|
+ c.JSON(403, gin.H{"error": "CSRF token验证失败"})
|
|
|
+ c.Abort()
|
|
|
+ },
|
|
|
+ })
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 5.5 实战项目:电商平台
|
|
|
+
|
|
|
+### 项目功能模块
|
|
|
+- 用户管理(注册、登录、个人信息)
|
|
|
+- 商品管理(CRUD、分类、搜索)
|
|
|
+- 购物车功能
|
|
|
+- 订单管理
|
|
|
+- 支付集成
|
|
|
+- 后台管理系统
|
|
|
+
|
|
|
+### 技术架构
|
|
|
+```go
|
|
|
+// 项目结构
|
|
|
+ecommerce-platform/
|
|
|
+├── cmd/
|
|
|
+│ ├── api/ # API服务器
|
|
|
+│ └── admin/ # 后台管理
|
|
|
+├── internal/
|
|
|
+│ ├── config/ # 配置管理
|
|
|
+│ ├── handler/ # HTTP处理器
|
|
|
+│ ├── middleware/ # 中间件
|
|
|
+│ ├── model/ # 数据模型
|
|
|
+│ ├── repository/ # 数据访问层
|
|
|
+│ ├── service/ # 业务逻辑层
|
|
|
+│ └── utils/ # 工具函数
|
|
|
+├── pkg/
|
|
|
+│ ├── auth/ # 认证模块
|
|
|
+│ ├── cache/ # 缓存模块
|
|
|
+│ ├── payment/ # 支付模块
|
|
|
+│ └── storage/ # 存储模块
|
|
|
+└── web/ # 前端资源
|
|
|
+ ├── static/
|
|
|
+ └── templates/
|
|
|
+```
|
|
|
+
|
|
|
+### 核心业务逻辑
|
|
|
+```go
|
|
|
+// 购物车服务
|
|
|
+type CartService struct {
|
|
|
+ cartRepo repository.CartRepository
|
|
|
+ productRepo repository.ProductRepository
|
|
|
+}
|
|
|
+
|
|
|
+func (cs *CartService) AddToCart(userID, productID uint, quantity int) error {
|
|
|
+ // 检查商品库存
|
|
|
+ product, err := cs.productRepo.FindByID(productID)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if product.Stock < quantity {
|
|
|
+ return errors.New("库存不足")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加商品到购物车
|
|
|
+ return cs.cartRepo.AddItem(userID, productID, quantity)
|
|
|
+}
|
|
|
+
|
|
|
+// 订单服务
|
|
|
+type OrderService struct {
|
|
|
+ orderRepo repository.OrderRepository
|
|
|
+ paymentRepo repository.PaymentRepository
|
|
|
+}
|
|
|
+
|
|
|
+func (os *OrderService) CreateOrder(userID uint, cartItems []model.CartItem) (*model.Order, error) {
|
|
|
+ // 计算总价
|
|
|
+ var totalAmount float64
|
|
|
+ for _, item := range cartItems {
|
|
|
+ totalAmount += item.Product.Price * float64(item.Quantity)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建订单
|
|
|
+ order := &model.Order{
|
|
|
+ UserID: userID,
|
|
|
+ TotalAmount: totalAmount,
|
|
|
+ Status: model.OrderStatusPending,
|
|
|
+ }
|
|
|
+
|
|
|
+ return os.orderRepo.Create(order)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 特训评估标准
|
|
|
+
|
|
|
+### 技术掌握程度 (40%)
|
|
|
+- Web框架熟练度
|
|
|
+- 架构设计能力
|
|
|
+- 代码质量
|
|
|
+
|
|
|
+### 项目完成度 (30%)
|
|
|
+- 功能完整性
|
|
|
+- 用户体验
|
|
|
+- 文档质量
|
|
|
+
|
|
|
+### 性能与安全 (20%)
|
|
|
+- 响应速度
|
|
|
+- 安全措施
|
|
|
+- 错误处理
|
|
|
+
|
|
|
+### 创新与扩展 (10%)
|
|
|
+- 技术创新
|
|
|
+- 扩展性设计
|
|
|
+- 代码复用
|
|
|
+
|
|
|
+## 学习建议
|
|
|
+
|
|
|
+1. **循序渐进**:从基础功能开始,逐步增加复杂度
|
|
|
+2. **实践为主**:每个知识点都要动手编码
|
|
|
+3. **代码审查**:定期检查代码质量
|
|
|
+4. **性能测试**:关注应用性能指标
|
|
|
+5. **安全意识**:始终考虑安全防护
|
|
|
+
|
|
|
+这个Web开发特训章节将帮助您系统掌握Go语言Web开发的全套技能,为成为全栈开发工程师奠定坚实基础。
|