[golang gin框架] 45.Gin商城项目-微服务实战之后台Rbac微服务之角色权限关联

角色和权限的关联关系在前面文章中有讲解,见[golang gin框架] 14.Gin 商城项目-RBAC管理之角色和权限关联,角色授权,在这里通过微服务来实现角色对权限的授权操作,这里要实现的有两个功能,一个是进入授权,另一个是,授权提交操作,页面如下:

 一.实现后台权限管理Rbac之角色权限关联微服务服务端功能

 这里需要在proto/rbacRole.proto中增加两个方法:授权操作 Auth(), 执行授权操作DoAuth(),具体参考[golang gin框架] 14.Gin 商城项目-RBAC管理_角色权限管理之增加授权方法,原始代码如下:

//授权
func (con RoleController) Auth(c *gin.Context) {//获取idid, err := models.Int(c.Query("id"))if err != nil {con.Error(c, "传入数据错误", "/admin/role")return}role := models.Role{Id: id}models.DB.Find(&role)//获取所有权限列表accessList := []models.Access{}models.DB.Where("module_id = ?", 0).Preload("AccessItem").Find(&accessList)//获取当前角色拥有的权限,并把权限id放在一个map对象中roleAccess := []models.RoleAccess{}models.DB.Where("role_id = ?", id).Find(&roleAccess)roleAccessMap := make(map[int]int)for _, v := range roleAccess {roleAccessMap[v.AccessId] = v.AccessId}//循环遍历所有权限数据,判断当前权限的id是否在角色权限的map对象中,如果是的话给当前数据加入checked属性for i := 0; i < len(accessList); i++ { //循环权限列表if _, ok := roleAccessMap[accessList[i].Id]; ok { // 判断当前权限是否在角色权限的map对象中accessList[i].Checked = true}for j := 0; j < len(accessList[i].AccessItem); j++ { // 判断当前权限的子栏位是否在角色权限的map中if _, ok := roleAccessMap[accessList[i].AccessItem[j].Id]; ok { // 判断当前权限是否在角色权限的map对象中accessList[i].AccessItem[j].Checked = true}}}c.HTML(http.StatusOK, "admin/role/auth.html", gin.H{"roleId":     id,"accessList": accessList,})
}//授权提交
func (con RoleController) DoAuth(c *gin.Context) {//获取提交的表单数据roleId, err := models.Int(c.PostForm("role_id"))if err != nil {con.Error(c, "传入数据错误", "/admin/role")return}//获取表单提交的权限id切片accessIds := c.PostFormArray("access_node[]")//先删除当前角色对应的权限roleAccess := models.RoleAccess{}models.DB.Where("role_id = ?", roleId).Delete(&roleAccess)//循环遍历accessIds,增加当前角色对应的权限for _, v := range accessIds {roleAccess.RoleId = roleIdaccessId, _ := models.Int(v)roleAccess.AccessId = accessIdmodels.DB.Create(&roleAccess)}con.Success(c, "角色授权成功", "/admin/role")
}

 1.在proto/rbacRole.proto下增加Auth(),DoAuth()方法相关代码

参考上述方法,在角色微服务的proto中的service RbacRole中增加Auth(),DoAuth()相关方法代码,代码如下:

//角色管理
service RbacRole {//获取角色rpc方法: 请求参数RoleGetRequest, 响应参数RoleGetResponserpc RoleGet(RoleGetRequest) returns (RoleGetResponse) {}//增加角色rpc方法: 请求参数RoleAddRequest, 响应参数RoleAddResponserpc RoleAdd(RoleAddRequest) returns (RoleAddResponse) {}//编辑角色rpc方法: 请求参数RoleEditRequest, 响应参数RoleEditResponserpc RoleEdit(RoleEditRequest) returns (RoleEditResponse) {}//删除角色rpc方法: 请求参数RoleDeleteRequest, 响应参数RoleDeleteResponserpc RoleDelete(RoleDeleteRequest) returns (RoleDeleteResponse) {}//授权rpc RoleAuth(RoleAuthRequest) returns (RoleAuthResponse) {}//授权提交rpc RoleDoAuth(RoleDoAuthRequest) returns (RoleDoAuthResponse) {}
}

这里需要实现角色授权请求方法RoleAuthRequest(),角色授权请求响应方法RoleAuthResponse(),通过上述Auth()方法可以确定:RoleAuthRequest()请求中需要传递一个角色id roleId,响应方法中返回权限切片,需要构建一个message AccessModel,代码如下:

//权限相关模型:参考models/access.go
message AccessModel{int64 id=1;string moduleName =2;string actionName=3;int64 type=4;string url=5;int64 moduleId=6;int64 sort =7;string description=8;int64 status=9;int64 addTime=10;bool checked=11;repeated AccessModel accessItem=12;
}//角色授权参数
message RoleAuthRequest{int64 roleId=1;
}
//角色授权响应参数
message RoleAuthResponse{repeated AccessModel accessList=1;
}

而实现角色授权提交请求方法RoleDoAuthRequest(),角色授权提交请求响应方法RoleDoAuthResponse(),通过上述Auth()方法可以确定:RoleDoAuthRequest请求中需要传递角色id roleId以及角色对应的权限id切片,响应方法中返回是否操作完成,代码如下:

//角色授权提交参数
message RoleDoAuthRequest{int64 roleId=1;repeated string accessIds=2;
}//角色授权提交响应参数
message RoleDoAuthResponse{bool success=1;string message=2;
}

 完整代码如下:

syntax = "proto3";package rbac;option go_package = "./proto/rbacRole";//角色管理
service RbacRole {//获取角色rpc方法: 请求参数RoleGetRequest, 响应参数RoleGetResponserpc RoleGet(RoleGetRequest) returns (RoleGetResponse) {}//增加角色rpc方法: 请求参数RoleAddRequest, 响应参数RoleAddResponserpc RoleAdd(RoleAddRequest) returns (RoleAddResponse) {}//编辑角色rpc方法: 请求参数RoleEditRequest, 响应参数RoleEditResponserpc RoleEdit(RoleEditRequest) returns (RoleEditResponse) {}//删除角色rpc方法: 请求参数RoleDeleteRequest, 响应参数RoleDeleteResponserpc RoleDelete(RoleDeleteRequest) returns (RoleDeleteResponse) {}//授权rpc RoleAuth(RoleAuthRequest) returns (RoleAuthResponse) {}//授权提交rpc RoleDoAuth(RoleDoAuthRequest) returns (RoleDoAuthResponse) {}
}//角色相关model
message RoleModel{int64 id=1;string title=2;string description=3;int64 status=4;int64 addTime =5;
}//权限相关模型:参考models/access.go
message AccessModel{int64 id=1;string moduleName =2;string actionName=3;int64 type=4;string url=5;int64 moduleId=6;int64 sort =7;string description=8;int64 status=9;int64 addTime=10;bool checked=11;repeated AccessModel accessItem=12;
}//获取角色请求参数
message RoleGetRequest{//角色idint64 id =1;
}//获取角色响应参数
message RoleGetResponse{//角色model切片repeated RoleModel roleList=1;
}//增加角色请求参数
message RoleAddRequest{//角色名称string title=1;//说明string description=2;//状态int64 status=3;//增加时间int64 addTime =4;
}//增加角色响应参数
message RoleAddResponse{//是否增加成功bool success=1;//返回状态说明string message=2;
}//编辑角色请求参数
message RoleEditRequest{//角色idint64 id=1;//角色名称string title=2;//说明string description=3;//状态int64 status=4;//增加时间int64 addTime =5;
}//编辑角色响应参数
message RoleEditResponse{	//是否编辑成功bool success=1;//返回状态说明string message=2;
}//删除角色请求参数
message RoleDeleteRequest{//角色idint64 id=1;
}//删除角色响应参数
message RoleDeleteResponse{	//是否删除成功bool success=1;//返回状态说明string message=2;
}//角色授权参数
message RoleAuthRequest{int64 roleId=1;
}
//角色授权响应参数
message RoleAuthResponse{repeated AccessModel accessList=1;
}//角色授权提交参数
message RoleDoAuthRequest{int64 roleId=1;repeated string accessIds=2;
}//角色授权提交响应参数
message RoleDoAuthResponse{bool success=1;string message=2;
}

2.编译一下rbacRole.proto

因为修改了rbacRole.proto,那么就需要再次使用命令protoc --proto_path=. --micro_out=. --go_out=:. proto/rbacRole.proto在在server/rbac下运行,重新编译,生成role相关pb.go,pb.micro.go文件

3.在handler/rbacRole.go文件中,实现新增的Auth(),DoAuth()方法

 在实现之前,需要在models下引入角色-权限关联模型:RoleAcces.go模型:

package models//角色-权限 关联表type RoleAccess struct {AccessId intRoleId   int
}func (RoleAccess) TableName() string {return "role_access"
}

然后才实现Auth(),DoAuth()方法,参考[golang gin框架] 14.Gin 商城项目-RBAC管理_角色权限关联 中的Auth(),DoAuth方法,并抽离其中的代码,在handler/rbacRole.go中实现方法中对应的逻辑,代码如下:

//授权
func (e *RbacRole) RoleAuth(ctx context.Context, req *pb.RoleAuthRequest, res *pb.RoleAuthResponse) error {//1、获取角色id  req.RoleId//获取所有权限列表accessList := []models.Access{}models.DB.Where("module_id = ?", 0).Preload("AccessItem").Find(&accessList)//获取当前角色拥有的权限,并把权限id放在一个map对象中roleAccess := []models.RoleAccess{}models.DB.Where("role_id = ?",  req.RoleId).Find(&roleAccess)roleAccessMap := make(map[int]int)for _, v := range roleAccess {roleAccessMap[v.AccessId] = v.AccessId}//循环遍历所有权限数据,判断当前权限的id是否在角色权限的map对象中,如果是的话给当前数据加入checked属性for i := 0; i < len(accessList); i++ { //循环权限列表if _, ok := roleAccessMap[int(accessList[i].Id)]; ok { // 判断当前权限是否在角色权限的map对象中, 需要进行类型转换accessList[i].Checked = true}for j := 0; j < len(accessList[i].AccessItem); j++ { // 判断当前权限的子栏位是否在角色权限的map中if _, ok := roleAccessMap[int(accessList[i].AccessItem[j].Id)]; ok { // 判断当前权限是否在角色权限的map对象中accessList[i].AccessItem[j].Checked = true}}}//处理数据:进行类型转换匹配操作var tempList []*pb.AccessModelfor _, v := range accessList {var tempItemList []*pb.AccessModelfor _, k := range v.AccessItem {tempItemList = append(tempItemList, &pb.AccessModel{Id:          int64(k.Id),ModuleName:  k.ModuleName,ActionName:  k.ActionName,Type:        int64(k.Type),Url:         k.Url,ModuleId:    int64(k.ModuleId),Sort:        int64(k.Sort),Description: k.Description,Status:      int64(k.Status),Checked:     k.Checked,AddTime:     int64(k.AddTime),})}tempList = append(tempList, &pb.AccessModel{Id:          int64(v.Id),ModuleName:  v.ModuleName,ActionName:  v.ActionName,Type:        int64(v.Type),Url:         v.Url,ModuleId:    int64(v.ModuleId),Sort:        int64(v.Sort),Description: v.Description,Status:      int64(v.Status),AddTime:     int64(v.AddTime),Checked:     v.Checked,AccessItem:  tempItemList,})}res.AccessList = tempListreturn nil
}//执行授权
func (e *RbacRole) RoleDoAuth(ctx context.Context, req *pb.RoleDoAuthRequest, res *pb.RoleDoAuthResponse) error {//先删除当前角色对应的权限roleAccess := models.RoleAccess{}models.DB.Where("role_id = ?", req.RoleId).Delete(&roleAccess)//循环遍历accessIds,增加当前角色对应的权限for _, v := range req.AccessIds {roleAccess.RoleId = int(req.RoleId)accessId, _ :=strconv.Atoi(v)roleAccess.AccessId = accessIdmodels.DB.Create(&roleAccess)}res.Success = trueres.Message = "授权成功"return nil
}

4.实现后台权限管理Rbac之角色权限关联微服务客户端调用功能

因为修改了rbacRole.proto文件,并重新编译了,故要复制server/rbac/proto文件夹下rbacRole.go以及rbacRole文件夹到client项目中的proto文件夹下

然后在controllers/admin/roleController.go中实现Auth(),DoAuth()调用角色-权限关联微服务方法,需要先删除Auth(),DoAuth()中旧的代码,然后增加新的角色-权限关联微服务方法,旧的方法见文章开头代码,新代码如下:


//授权
func (con RoleController) Auth(c *gin.Context) {//获取idid, err := models.Int(c.Query("id"))if err != nil {con.Error(c, "传入数据错误", "/admin/role")return}role := models.Role{Id: id}models.DB.Find(&role)//调用微服务获取角色授权相关数据rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)res, _ := rbacClient.RoleAuth(context.Background(), &pbRbac.RoleAuthRequest{RoleId: int64(id),})c.HTML(http.StatusOK, "admin/role/auth.html", gin.H{"roleId":     id,"accessList": res.AccessList,})
}//授权提交
func (con RoleController) DoAuth(c *gin.Context) {//获取提交的表单数据roleId, err := models.Int(c.PostForm("role_id"))if err != nil {con.Error(c, "传入数据错误", "/admin/role")return}//获取表单提交的权限id切片accessIds := c.PostFormArray("access_node[]")//调用微服务执行授权rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)res, _ := rbacClient.RoleDoAuth(context.Background(), &pbRbac.RoleDoAuthRequest{RoleId:    int64(roleId),AccessIds: accessIds,})if res.Success {con.Success(c, "角色授权成功", "/admin/role")return}con.Error(c, "授权失败", "/admin/role/auth?id="+models.String(roleId))
}

完整的roleController.go代码如下:

package adminimport ("context""github.com/gin-gonic/gin""goshop/models"pbRbac "goshop/proto/rbacRole""net/http""strings"
)type RoleController struct {BaseController
}//角色列表
func (con RoleController) Index(c *gin.Context) {//调用Rbac微服务rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)res, _ := rbacClient.RoleGet(context.Background(), &pbRbac.RoleGetRequest{})c.HTML(http.StatusOK, "admin/role/index.html", gin.H{"roleList": res.RoleList,})
}//新增角色
func (con RoleController) Add(c *gin.Context) {c.HTML(http.StatusOK, "admin/role/add.html", gin.H{})
}//新增角色:提交
func (con RoleController) DoAdd(c *gin.Context) {//获取表单的提交数据//strings.Trim(str, cutset), 去除字符串两边的cutset字符title := strings.Trim(c.PostForm("title"), " ") // 去除字符串两边的空格description := strings.Trim(c.PostForm("description"), " ")//判断角色名称是否为空if title == "" {con.Error(c, "角色名称不能为空", "/admin/role/add")return}//调用微服务,实现角色的添加rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)res, _ := rbacClient.RoleAdd(context.Background(), &pbRbac.RoleAddRequest{Title:       title,Description: description,AddTime:     models.GetUnix(),Status:      1,})if !res.Success {con.Error(c, "增加角色失败 请重试", "/admin/role/add")} else {con.Success(c, "增加角色成功", "/admin/role")}
}//编辑角色
func (con RoleController) Edit(c *gin.Context) {//获取角色idid, err := models.Int(c.Query("id"))if err != nil {con.Error(c, "传入数据错误", "/admin/role")} else {//调用微服务,获取角色信息rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)res, _ := rbacClient.RoleGet(context.Background(), &pbRbac.RoleGetRequest{Id: int64(id),})c.HTML(http.StatusOK, "admin/role/edit.html", gin.H{"role": res.RoleList[0],})}
}//编辑角色:提交
func (con RoleController) DoEdit(c *gin.Context) {//获取提交的表单数据id, err := models.Int(c.PostForm("id"))if err != nil {con.Error(c, "传入数据错误", "/admin/role")return}//获取表单的提交数据//strings.Trim(str, cutset), 去除字符串两边的cutset字符title := strings.Trim(c.PostForm("title"), " ") // 去除字符串两边的空格description := strings.Trim(c.PostForm("description"), " ")//判断角色名称是否为空if title == "" {con.Error(c, "角色名称不能为空", "/admin/role/add")return}//调用微服务修改rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)res, _ := rbacClient.RoleEdit(context.Background(), &pbRbac.RoleEditRequest{Id:          int64(id),Title:       title,Description: description,})if !res.Success {con.Error(c, "修改数据失败", "/admin/role/edit?id="+models.String(id))return}con.Success(c, "修改数据成功", "/admin/role")
}//删除角色
func (con RoleController) Delete(c *gin.Context) {//获取提交的表单数据id, err := models.Int(c.Query("id"))if err != nil {con.Error(c, "传入数据错误", "/admin/role")return}rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)res, _ := rbacClient.RoleDelete(context.Background(), &pbRbac.RoleDeleteRequest{Id: int64(id),})if res.Success {con.Success(c, "删除数据成功", "/admin/role")return}con.Error(c, "删除数据失败", "/admin/role")
}//授权
func (con RoleController) Auth(c *gin.Context) {//获取idid, err := models.Int(c.Query("id"))if err != nil {con.Error(c, "传入数据错误", "/admin/role")return}role := models.Role{Id: id}models.DB.Find(&role)//调用微服务获取角色授权相关数据rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)res, _ := rbacClient.RoleAuth(context.Background(), &pbRbac.RoleAuthRequest{RoleId: int64(id),})c.HTML(http.StatusOK, "admin/role/auth.html", gin.H{"roleId":     id,"accessList": res.AccessList,})
}//授权提交
func (con RoleController) DoAuth(c *gin.Context) {//获取提交的表单数据roleId, err := models.Int(c.PostForm("role_id"))if err != nil {con.Error(c, "传入数据错误", "/admin/role")return}//获取表单提交的权限id切片accessIds := c.PostFormArray("access_node[]")//调用微服务执行授权rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)res, _ := rbacClient.RoleDoAuth(context.Background(), &pbRbac.RoleDoAuthRequest{RoleId:    int64(roleId),AccessIds: accessIds,})if res.Success {con.Success(c, "角色授权成功", "/admin/role")return}con.Error(c, "授权失败", "/admin/role/auth?id="+models.String(roleId))
}

 二.校验权限管理Rbac之角色权限关联微服务功能

参考[golang gin框架] 43.Gin商城项目-微服务实战之后台Rbac微服务之管理员的增删改查以及管理员和角色关联

1.先启动服务端

见[golang gin框架] 40.Gin商城项目-微服务实战之Captcha验证码微服务代码, 这里还要启动验证码captcha微服务服务端代码以及权限管理Rbac微服务(用户登录微服务服务端,角色管理微服务服务端,管理员管理微服务服务端)服务端才行

 

2.启动客户端

在项目根目录下运行 :go run main.go,启动项目

3.校验权限管理Rbac之角色权限关联微服务操作是否成功 

访问后台登录页面,输入用户名,密码,验证码,登录到后台后,进入角色管理列表页面,对角色进行授权操作

 

 

好了,权限管理Rbac之角色权限关联微服务功能客户端操作完成,下面一节讲解:修改商城客户端调用微服务的权限验证以及Rbac微服务数据库抽离功能

[上一节][golang gin框架] 44.Gin商城项目-微服务实战之后台Rbac微服务之权限的增删改查微服务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/57039.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JavaEE】Spring Boot - 日志文件

【JavaEE】Spring Boot 开发要点总结&#xff08;3&#xff09; 文章目录 【JavaEE】Spring Boot 开发要点总结&#xff08;3&#xff09;1. 日志有什么作用2. 日志格式2.1 日志框架原理 3. 日志的打印3.1 System.out.println3.2 使用日志框架3.3 日志级别3.3.1 设置默认日志显…

关于接口测试用例设计的一些思考

接口测试发现的典型问题 传入参数处理不当&#xff0c;引起程序错误类型溢出&#xff0c;导致数据读取和写入不一致对象权限校验出错&#xff0c;可获取其他角色信息状态出错&#xff0c;导致逻辑处理出现问题逻辑校验不完善定时任务执行出错 接口测试用例设计 接口测试用例…

Python连接Hive实例教程

一 Python连接hive环境实例 经在网络查询相关的教程&#xff0c;发现有好多的例子&#xff0c;发现连接底层用的的驱动基本都是pyhive和pyhs2两种第三方库的来连接的 hive,下面将简介windows 10 python 3.10 连接hive的驱动程序方式&#xff0c;开发工具&#xff1a;pycharm …

Android 网络协议与网络编程

一、TCP/IP协议 Transmission Control Protocol/Internet Protocol的简写&#xff0c;中译名为传输控制协议/因特网互联 协议&#xff0c;是Internet最基本的协议、Internet国际互联网络的基础&#xff0c;由网络层的IP协议和传输层的TCP 协议组成。协议采用了4层的层级结构。…

golang函数传参——值传递理解

做了五年的go开发&#xff0c;却并没有什么成长&#xff0c;都停留在了业务层面了。一直以为golang中函数传参&#xff0c;如果传的是引用类型&#xff0c;则是以引用传递&#xff0c;造成这样的误解&#xff0c;实在也不能怪我。我们来看一个例子&#xff0c;众所周知&#xf…

时序预测 | MATLAB实现BO-LSTM贝叶斯优化长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现BO-LSTM贝叶斯优化长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现BO-LSTM贝叶斯优化长短期记忆神经网络时间序列预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-LSTM贝叶斯优化长短期记忆神经网络时间序列预…

Codejock Skin Framework Visual C++ MFC Crack

Codejock Skin Framework Visual C MFC Crack Codejock Visual CMFC皮肤框架为Windows开发人员提供了一种高度复杂的应用程序皮肤框架技术&#xff0c;该技术是在考虑Windows主题(视觉样式)的情况下开发的。只需几行代码就可以实现一个完全主题化的应用程序。 功能概述 Codejoc…

mysql 数据库引擎介绍

一、数据库引擎 数据库引擎是用于存储、处理和保护数据的核心服务。利用数据库引擎可控制访问权限并快速处理事务&#xff0c;从而满足企业内大多数需要处理大量数据的应用程序的要求。 使用数据库引擎创建用于联机事务处理或联机分析处理数据的关系数据库。这包括创建用于存储…

Linux系统编程(线程同步 互斥锁)

文章目录 前言一、什么是线程同步二、不使用线程同步访问共享资源可能出现的问题三、互斥锁概念四、互斥锁使用1.初始化线程锁的方式2.使用代码 五、死锁的产生和解决方法1.什么是死锁2.为什么会产生死锁3.怎么解决死锁问题 总结 前言 本篇文章带大家学习线程的同步。 一、什…

Laravel 框架资源嵌套.浅嵌套.自定义表单伪造.CSRF 保护 ④

![请添加图片描述](https://img-blog.csdnimg.cn/154d035aa4db42df99f3b01fbf287e46.gif#pic_center)作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; THINK PHP &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&a…

如何使用ONLYOFFICE+ffmpeg来给视频文件打马赛克

如何使用ONLYOFFICEffmpeg来给视频文件打马赛克 我这里之前写过很多关于ONLYOFFICE使用、安装的系列图文&#xff0c;也写过很多关于ffmpeg使用的图文&#xff0c;那么这次继续&#xff0c;把这两个开源软件放在一起&#xff0c;能碰撞出什么火花般的功能来。 这就是给视频文…

401 · 排序矩阵中的从小到大第k个数

链接&#xff1a;LintCode 炼码 - ChatGPT&#xff01;更高效的学习体验&#xff01; 题解&#xff1a; 九章算法 - 帮助更多程序员找到好工作&#xff0c;硅谷顶尖IT企业工程师实时在线授课为你传授面试技巧 class Solution { public:/*** param matrix: a matrix of intege…