v6: 完整图书平台 — 全栈集成一、版本概述v6 在 v5 微服务基础上新增user-service用户认证、Bleve 全文搜索和前端界面形成完整的前后端分离平台。相比 v5 的核心变化用户服务user-service(:8083)注册/登录/JWT 认证JWT 认证golang-jwt 生成和验证 Token中间件保护路由全文搜索Bleve 搜索引擎支持中文分词(CJK)前端界面HTML CSS JS图书管理/搜索/借阅/登录完善 gRPCborrow-service 真正调用 book-service 的 gRPC 接口bcrypt 密码密码哈希存储不存明文项目结构v6/ ├── backend/ │ ├── book-service/ │ │ ├── main.go │ │ ├── model/book.go # GORM Book │ │ ├── repository/ │ │ │ ├── db.go # MySQL │ │ │ └── book_repo.go # CRUD 库存操作 │ │ ├── cache/redis.go # Redis 缓存 分布式锁 │ │ ├── search/bleve.go # Bleve 全文搜索新增 │ │ ├── service/book_service.go │ │ ├── handler/ │ │ │ ├── http_handler.go # HTTP API 搜索路由 │ │ │ └── grpc_handler.go # gRPC 服务端 │ │ └── Dockerfile │ ├── borrow-service/ │ │ ├── main.go # gRPC Client 注入 │ │ ├── model/borrow_record.go │ │ ├── repository/ │ │ │ ├── db.go │ │ │ └── borrow_repo.go │ │ ├── handler/ │ │ │ ├── grpc_client.go # gRPC 客户端完善实现 │ │ │ └── http_handler.go │ │ ├── service/borrow_service.go # BookStockClient 接口 补偿事务 │ │ └── Dockerfile │ └── user-service/ # 新增服务 │ ├── main.go │ ├── model/user.go # GORM User │ ├── repository/ │ │ ├── db.go │ │ └── user_repo.go │ ├── service/user_service.go # JWT bcrypt │ ├── handler/http_handler.go │ ├── middleware/auth.go # JWT 认证中间件 │ └── Dockerfile ├── frontend/ │ ├── index.html │ ├── css/style.css │ └── js/ │ ├── api.js # API 封装 │ └── main.js # 交互逻辑 ├── proto/ │ ├── book.proto │ └── book/ │ ├── book.pb.go │ └── book_grpc.pb.go └── docker-compose.yml二、核心代码解读user-service/service/user_service.go — JWT 认证func(s*userService)Login(username,passwordstring)(*LoginResult,error){user,err:s.repo.GetByUsername(username)iferr!nil{returnnil,ErrUserNotFound}// bcrypt 校验密码iferr:bcrypt.CompareHashAndPassword([]byte(user.Password),[]byte(password));err!nil{returnnil,ErrInvalidPassword}// 生成 JWT Tokentoken:jwt.NewWithClaims(jwt.SigningMethodHS256,jwt.MapClaims{user_id:user.ID,username:user.Username,exp:time.Now().Add(24*time.Hour).Unix(),})tokenString,_:token.SignedString(jwtSecret)returnLoginResult{Token:tokenString,UserID:user.ID},nil}设计要点bcrypt自适应哈希同一个密码每次生成的哈希值不同防止彩虹表攻击JWT Claims包含 user_id、username、exp(过期时间)客户端携带 Token 访问受保护资源HS256对称签名适合单服务端场景生产环境可考虑 RS256(非对称)user-service/middleware/auth.go — 认证中间件funcAuthMiddleware(userSvc service.UserService)func(http.Handler)http.Handler{returnfunc(next http.Handler)http.Handler{returnhttp.HandlerFunc(func(w http.ResponseWriter,r*http.Request){authHeader:r.Header.Get(Authorization)parts:strings.Split(authHeader, )iflen(parts)!2||parts[0]!Bearer{http.Error(w,Token格式错误,http.StatusUnauthorized)return}userID,err:userSvc.VerifyToken(parts[1])iferr!nil{http.Error(w,无效的Token,http.StatusUnauthorized)return}ctx:context.WithValue(r.Context(),UserIDKey,userID)next.ServeHTTP(w,r.WithContext(ctx))})}}设计要点Bearer Token标准 Authorization 头格式Bearer tokenContext 传递解析出的 userID 存入 context后续 handler 可通过GetUserID(ctx)获取中间件模式与 v3 的 Logger/Recover 一致可灵活组合book-service/search/bleve.go — 全文搜索funcInitSearch()error{indexPath:os.Getenv(SEARCH_INDEX_PATH)ifindexPath{execPath,_:os.Getwd()indexPathfilepath.Join(execPath,search_index)}// 创建或打开索引mapping:bleve.NewIndexMapping()mapping.AddDocumentMapping(book,createBookMapping())idx,_:bleve.New(indexPath,mapping)indexidx}funcSearchBooks(keywordstring)([]model.Book,error){// 标题搜索(CJK分词) 作者搜索(标准分词)取并集titleQuery:bleve.NewMatchQuery(keyword)titleQuery.SetField(title)authorQuery:bleve.NewMatchQuery(keyword)authorQuery.SetField(author)disjunction:bleve.NewDisjunctionQuery(titleQuery,authorQuery)// ...}设计要点CJK 分词中文日文韩文分词器支持中文搜索DisjunctionQuery标题或作者匹配任一即返回OR 逻辑索引路径支持环境变量配置默认当前目录borrow-service/handler/grpc_client.go — 真正的 gRPC 调用func(c*GRPCClient)DecreaseStock(ctx context.Context,bookIDint64)(int,error){conn,err:grpc.NewClient(c.bookServiceAddr,grpc.WithTransportCredentials(insecure.NewCredentials()))iferr!nil{return0,err}deferconn.Close()client:pb.NewBookServiceClient(conn)resp,err:client.DecreaseStock(ctx,pb.DecreaseStockRequest{BookId:bookID})iferr!nil{return0,err}returnint(resp.Available),nil}对比 v5v5 的 gRPC 客户端在 main.go 启动时建立连接并复用v6 使用grpc.NewClient替代已废弃的grpc.Dial每次请求创建连接简化实现生产环境应复用连接borrow-service/service/borrow_service.go — 接口化 gRPC 客户端typeBookStockClientinterface{DecreaseStock(ctx context.Context,bookIDint64)(int,error)IncreaseStock(ctx context.Context,bookIDint64)(int,error)}typeborrowServicestruct{repo borrowRepository// 接口而非具体类型stockClient BookStockClient// 可 mock 的接口}设计要点接口抽象BookStockClient 接口使 borrowService 不依赖具体 gRPC 实现便于测试borrowRepository 接口同样抽象化测试时可 mock 整个数据层frontend/js/api.js — 前端 API 封装constAPI_BASE{user:http://localhost:8083,book:http://localhost:8081,borrow:http://localhost:8082};asyncfunctionapiCall(service,method,path,bodynull){consturl${API_BASE[service]}${path};constoptions{method,headers:{Content-Type:application/json}};if(authToken)options.headers[Authorization]Bearer${authToken};if(body)options.bodyJSON.stringify(body);constresponseawaitfetch(url,options);returnawaitresponse.json();}设计要点统一 API 封装apiCall函数处理认证头、请求体、错误处理函数命名隔离apiRegister、apiLogin等加api前缀避免与 main.js 的事件处理函数重名三、与 v5 的对比特性v5v6服务数量2 (bookborrow)3 (user)认证无JWT bcrypt搜索无Bleve 全文搜索前端无HTML/CSS/JSgRPC 客户端功能完整完善实现接口化密码存储无bcrypt 哈希接口可测试性具体类型接口化(mock友好)四、设计思想思想体现认证授权JWT 无状态认证服务端不保存 session接口隔离BookStockClient/borrowRepository 接口依赖倒置前后端分离前端 SPA 后端 RESTful API全文搜索独立搜索引擎与数据库解耦渐进增强每个版本只在前一版本基础上增加必要功能五、版本演进总结v1 单文件录入 → v2 分层CRUD → v3 HTTP服务 → v4 并发借阅 → v5 微服务 → v6 全栈平台 文件I/O JSON持久化 Service层 MutexContext MySQLRedis JWT认证 纯函数测试 RWMutex 中间件 优雅关闭 gRPC通信 全文搜索 RESTful 补偿事务 分布式锁 前端界面 Docker部署 bcrypt密码核心演进路线v1→v2从能跑到可维护结构化分层v2→v3从本地工具到网络服务HTTPService层v3→v4从单用户到多用户并发MutexContext补偿事务v4→v5从单体到分布式微服务MySQLRedisgRPCv5→v6从后端到全栈认证搜索前端接口化每个版本都是在前一个版本的基础上最小增量地引入新概念让学习者能清晰地看到每个架构决策的来龙去脉。