前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SaaS-系统用户权限设计(组织机构管理)

SaaS-系统用户权限设计(组织机构管理)

作者头像
cwl_java
发布2020-01-02 11:40:26
2.9K0
发布2020-01-02 11:40:26
举报
文章被收录于专栏:cwl_Javacwl_Java

第3章-SaaS系统用户权限设计

1 组织机构管理

1.1 需求分析

1.1.1 需求分析

实现企业组织结构管理,实现部门的基本CRUD操作

在这里插入图片描述
在这里插入图片描述

1.1.2 数据库表设计

代码语言:javascript
复制
CREATE TABLE `co_department` (
 `id` varchar(40) NOT NULL,
`company_id` varchar(255) NOT NULL COMMENT '企业ID',
`parent_id` varchar(255) DEFAULT NULL COMMENT '父级部门ID',
`name` varchar(255) NOT NULL COMMENT '部门名称',
`code` varchar(255) NOT NULL COMMENT '部门编码',
`category` varchar(255) DEFAULT NULL COMMENT '部门类别',
`manager_id` varchar(255) DEFAULT NULL COMMENT '负责人ID',
`city` varchar(255) DEFAULT NULL COMMENT '城市',
`introduce` text COMMENT '介绍',
`create_time` datetime NOT NULL COMMENT '创建时间',
`manager` varchar(40) DEFAULT NULL COMMENT '部门负责人',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

1.2 微服务实现

1.2.1 抽取公共代码

(1) 在公共controller

添加公共controller

代码语言:javascript
复制
import org.springframework.web.bind.annotation.ModelAttribute;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class BaseController {

    protected HttpServletRequest request;
    protected HttpServletResponse response;
    protected String companyId;
    protected String companyName;

    @ModelAttribute
    public void setResAnReq(HttpServletRequest request,HttpServletResponse response) {
        this.request = request;
        this.response = response;
        /**
         * 目前使用 companyId = 1
         *         companyName = "传智播客"
         */
        this.companyId = "1";
        this.companyName = "传智播客";
    }

}
(2) 公共service

添加公共BaseService

代码语言:javascript
复制
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

public class BaseService<T> {

    protected Specification<T> getSpec(String companyId) {
        Specification<T> spect = new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) {
                //根据企业id查询
                return cb.equal(root.get("companyId").as(String.class),companyId);
            }
        };
        return spect;
    }
}

1.2.2 实现基本CRUD操作

(1)实体类

创建Department实体类

代码语言:javascript
复制
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
 * (Department)实体类
 */
@Entity
@Table(name = "co_department")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department implements Serializable {
    private static final long serialVersionUID = -9084332495284489553L;
    //ID
    @Id
    private String id;
    /**
     * 父级ID
     */
    private String pid;
    /**
     * 企业ID
     */
    private String companyId;
    /**
     * 部门名称
     */
    private String name;
    /**
     * 部门编码,同级部门不可重复
     */
    private String code;

    /**
     * 负责人ID
     */
    private String managerId;
    /**
	*  负责人名称
	*/
    private String manager;

    /**
     * 介绍
     */
    private String introduce;
    /**
     * 创建时间
     */
    private Date createTime;
}
(2)持久化层

创建DepartmentDao

代码语言:javascript
复制
import com.ihrm.domain.company.Department;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
 * 部门dao接口
 */
public interface DepartmentDao extends JpaRepository<Department,String> ,JpaSpecificationExecutor<Department> {
}
(3)业务层

创建DepartmentService

代码语言:javascript
复制
import com.ihrm.common.service.BaseService;
import com.ihrm.common.utils.IdWorker;
import com.ihrm.company.dao.DepartmentDao;
import com.ihrm.domain.company.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.List;

@Service
public class DepartmentService extends BaseService {

    @Autowired
    private DepartmentDao departmentDao;

    @Autowired
    private IdWorker idWorker;

    /**
     * 1.保存部门
     */
    public void save(Department department) {
        //设置主键的值
        String id = idWorker.nextId()+"";
        department.setId(id);
        //调用dao保存部门
        departmentDao.save(department);
    }

    /**
     * 2.更新部门
     */
    public void update(Department department) {
        //1.根据id查询部门
        Department dept = departmentDao.findById(department.getId()).get();
        //2.设置部门属性
        dept.setCode(department.getCode());
        dept.setIntroduce(department.getIntroduce());
        dept.setName(department.getName());
        //3.更新部门
        departmentDao.save(dept);
    }

    /**
     * 3.根据id查询部门
     */
    public Department findById(String id) {
        return departmentDao.findById(id).get();
    }

    /**
     * 4.查询全部部门列表
     */
    public List<Department> findAll(String companyId) {
        /**
         * 用户构造查询条件
         *      1.只查询companyId
         *      2.很多的地方都需要根据companyId查询
         *      3.很多的对象中都具有companyId
         *
         */
//        Specification<Department> spec = new Specification<Department>() {
//            /**
//             * 用户构造查询条件
//             *      root   :包含了所有的对象数据
//             *      cq     :一般不用
//             *      cb     :构造查询条件
//             */
//            public Predicate toPredicate(Root<Department> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
//                //根据企业id查询
//                return cb.equal(root.get("companyId").as(String.class),companyId);
//            }
//        };
        return departmentDao.findAll(getSpec(companyId));
    }

    /**
     * 5.根据id删除部门
     */
    public void deleteById(String id) {
        departmentDao.deleteById(id);
    }
}
(4)控制层

创建控制器类DepartmentController

代码语言:javascript
复制
import com.ihrm.common.controller.BaseController;
import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.company.service.CompanyService;
import com.ihrm.company.service.DepartmentService;
import com.ihrm.domain.company.Company;
import com.ihrm.domain.company.Department;
import com.ihrm.domain.company.response.DeptListResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

//1.解决跨域
@CrossOrigin
//2.声明restContoller
@RestController
//3.设置父路径
@RequestMapping(value="/company")   //  company/deparment
public class DepartmentController extends BaseController{

    @Autowired
    private DepartmentService departmentService;

    @Autowired
    private CompanyService companyService;
    /**
     * 保存
     */
    @RequestMapping(value="/department",method = RequestMethod.POST)
    public Result save(@RequestBody Department department) {
        //1.设置保存的企业id
        /**
         * 企业id:目前使用固定值1,以后会解决
         */
        department.setCompanyId(companyId);
        //2.调用service完成保存企业
        departmentService.save(department);
        //3.构造返回结果
        return new Result(ResultCode.SUCCESS);
    }

    /**
     * 查询企业的部门列表
     * 指定企业id
     */
    @RequestMapping(value="/department",method = RequestMethod.GET)
    public Result findAll() {
        //1.指定企业id
        Company company = companyService.findById(companyId);
        //2.完成查询
        List<Department> list = departmentService.findAll(companyId);
        //3.构造返回结果
        DeptListResult deptListResult = new DeptListResult(company,list);
        return new Result(ResultCode.SUCCESS,deptListResult);
    }

    /**
     * 根据ID查询department
     */
    @RequestMapping(value="/department/{id}",method = RequestMethod.GET)
    public Result findById(@PathVariable(value="id") String id) {
        Department department = departmentService.findById(id);
        return new Result(ResultCode.SUCCESS,department);
    }

    /**
     * 修改Department
     */
    @RequestMapping(value="/department/{id}",method = RequestMethod.PUT)
    public Result update(@PathVariable(value="id") String id,@RequestBody Department department) {
        //1.设置修改的部门id
        department.setId(id);
        //2.调用service更新
        departmentService.update(department);
        return new Result(ResultCode.SUCCESS);
    }

    /**
     * 根据id删除
     */
    @RequestMapping(value="/department/{id}",method = RequestMethod.DELETE)
    public Result delete(@PathVariable(value="id") String id) {
        departmentService.deleteById(id);
        return new Result(ResultCode.SUCCESS);
    }
}

1.3 前端实现

1.3.1 创建模块

(1)使用命令行创建module-departments模块并引入到工程中
代码语言:javascript
复制
itheima moduleAdd departments
(2) 在src/main.js 中注册模块
代码语言:javascript
复制
import departments from '@/module-departments/' // 组织机构管理
Vue.use(departments, store)
(3)在 /module-departments/router/index.js 配置路由
代码语言:javascript
复制
import Layout from '@/module-dashboard/pages/layout'
const _import = require('@/router/import_' + process.env.NODE_ENV)
export default [
 {
    root: true,
    path: '/departments',
    component: Layout,
    redirect: 'noredirect',
    name: 'departments',
    meta: {
      title: '组织架构管理',
      icon: 'architecture'
   },
    children: [
     {
        path: 'index',
        component: _import('departments/pages/index'),
        name: 'organizations-index',
        meta: {title: '组织架构', icon: 'architecture', noCache: true}
     }
   ]
 }
]

1.3.2 配置请求API

在 /src/api/base/ 创建departments.js作为组织机构管理的API公共接口方法

代码语言:javascript
复制
import {
  createAPI, createFileAPI
} from '@/utils/request'
export const organList = data => createAPI('/company/departments', 'get', data)
export const add = data => createAPI('/company/departments', 'post', data)
export const update = data => createAPI(`/company/departments/${data.id}`, 'put', data)
export const detail = data => createAPI(`/company/departments/${data.id}`, 'get', data)
export const remove = data => createAPI(`/company/departments/${data.id}`, 'delete', 
data)
export const changeDept = data => createAPI(`/company/departments/changeDept`, 'put', 
data)
export const saveOrUpdate = data => {return data.id?update(data):add(data)}

1.3.3 构造列表

(1)构造基本页面样式

找到 /module-departments/page/index.vue ,使用element-ui提供的Card组件构造卡片式容器

代码语言:javascript
复制
<template>
  <div class="dashboard-container">
    <div class="app-container">
      <el-card shadow="never">
            <div class='organization-index'>
              <div class='organization-index-top'>
                <div class='main-top-title'>
                  <el-tabs v-model="activeName">
                    <el-tab-pane label="组织结构" name="first"></el-tab-pane>
                    <div class="el-tabs-report">
                      <a class="el-button el-button--primary el-button--mini" title="导 出" >导入</a>
                      <a class="el-button el-button--primary el-button--mini" title="导 出" >导出</a>
                    </div>
                  </el-tabs>
                </div>
              </div>
              <div style="overflow: scroll;white-space:nowrap"  class="treBox">
                <div class="treeCon clearfix">
                    <span>
                      <i class="fa fa-university" aria-hidden="true"></i>
                      <span ><strong>{{departData.name}}</strong></span>
                    </span>
                    <div class="fr">
                        <div class="treeRinfo">
                         <span>负责人</span>
                          <span>在职  <em class="colGreen" title="在职人数">---
</em>&nbsp;&nbsp;(<em class="colGreen" title="正式员工">---</em>&nbsp;/&nbsp;<em
class="colRed" title="非正式员工">---</em>)</span>
                        </div>
                        <div class="treeRinfo">
                          <el-dropdown class="item">
                            <span class="el-dropdown-link">
                             操作<i class="el-icon-arrow-down el-icon--right"></i>
                            </span>
                            <el-dropdown-menu slot="dropdown">
                                <el-dropdown-item>
                                  <el-button type="text" @click="handlAdd('')">添加子部门
</el-button>
                                </el-dropdown-item>
                              <el-dropdown-item>
                                <el-button type="text"
@click="handleList(organizationTree,0)">查看待分配员工</el-button>
                              </el-dropdown-item>
                            </el-dropdown-menu>
                          </el-dropdown>
                        </div>  
                    </div>
                  </div>
                  <!-- 
                    构造树形列表
                  -->
              </div>
            </div>    
      </el-card>
    </div>
</div>
</template>
<style rel="stylesheet/scss" lang="scss">
.el-dropdown {
  color: #000000
}
.el-tree-node__content>.el-tree-node__expand-icon {
  padding:0px; }
.el-tree-node__expand-icon {
  color:#ffffff
}
.generalClassNode {
  padding-left: 20px; }
.el-tree-node__content{
  font-size: 16px;
  line-height: 36px;
  height:36px; }
  .custom-tree-node{
  padding-left: 20px; }
.objectTree {
  overflow: auto;
  z-index: 100;
  width: 300px;
  border: 1px solid #dcdfe6;
  margin-top: 5px;
  left: 70px; }
.el-tabs__content {
  overflow: initial; }
.boxpad {
  margin-left: -40px; }
.boxpad > div:first-child,
.objectTree > div:first-child.el-tree-node > div:first-child {
  display: none; }
</style>
<style  rel="stylesheet/scss" lang="scss" scoped>
.el-tree-node__expand-icon{ }
.el-icon-caret-right{}
.el-tree-node__content{
  font-size: 14px;
  line-height: 36px; }
.generalClass {
  font-size: 14px;
  line-height: 36px;
  color:#000000
}
.all {
  position: relative;
  min-height: 100%;
  padding-bottom: 200px; }
.organization-main:after,
.organization-index-top:after {
  display: block;
  clear: both;
  content: '';
  visibility: hidden;
  height: 0; }
.organization-main {
  font-size: 14px;
  font-size: 14px; }
  .organization-index {
  padding-bottom: 20px;
  margin-left: 20px; }
.main-top-title {
  padding-left: 20px;
  padding-top: 20px;
  text-align: left; }
::-webkit-scrollbar-thumb {
  background-color: #018ee8;
  height: 50px;
  outline-offset: -2px;
  outline: 8px solid #fff;
  -webkit-border-radius: 4px; }
::-webkit-scrollbar-track-piece {
  background-color: #fff;
  -webkit-border-radius: 0; }
::-webkit-scrollbar {
  width: 8px;
  height: 8px; }
::-webkit-scrollbar-thumb:hover {
  background-color: #fb4446;
  height: 50px;
  -webkit-border-radius: 4px; }
.modal-total {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  background: #000;
  z-index: 90;
  opacity: 0.2; }
.modal {
  width: 400px;
  height: 300px;
  background-color: #ffffff;
  z-index: 999;
  position: absolute;
  left: 45%;
  top: 20%;
  text-align: center; }
.treBox {
  padding: 30px 120px 0;
  }
.organization-index-top {
  position: relative;
  .el-tabs-report {
    position: absolute;
    top: -50px;
    right: 15px;
 }
}
.treeCon {
  border-bottom: 1px solid #cfcfcf;
  padding: 10px 0;
  margin-bottom: 10px;
  .el-dropdown {
    color: #333;
 }
}
.treeRinfo {
  display: inline-block; }
.treeRinfo span {
  padding-left: 30px; }
</style>
(2)树形机构列表
代码语言:javascript
复制
 <el-tree :data="departData.children" :indent="20">
                  <div class="generalClass" slot-scope="{node,data}" style="width:99%">
                    <span>
                      <span>
                        <span>
                            <i v-if="node.isLeaf" class="fa fa-male"></i>
                            <i v-else :class="node.expanded ? 'fa fa-minus-square-o': 
'fa fa-plus-square-o'"></i>
                            <span><strong>{{ data.name }}</strong></span>
                        </span>                      
                      </span>                    
                    </span>
                    <div class=fr>
                      <span class="treeRinfo">
                        <div class="treeRinfo">
                          <span>负责人</span>
                          <span>在职  <em class="colGreen" title="在职人数">---
</em>&nbsp;&nbsp;(<em class="colGreen" title="正式员工">---</em>&nbsp;/&nbsp;<em
class="colRed" title="非正式员工">---</em>)</span>   
                        </div>
                        <el-dropdown class="item">
                          <span class="el-dropdown-link">
                           操作<i class="el-icon-arrow-down el-icon--right"></i>
                          </span>
                          <el-dropdown-menu slot="dropdown">
                           <el-dropdown-item>
                                <el-button type="text" @click="handlAdd(data.id)">添加子
部门</el-button>
                              </el-dropdown-item>
                              <el-dropdown-item>
                                <el-button type="text" @click="handleEdit(data.id)">编辑
部门</el-button>
                              </el-dropdown-item>
                            <el-dropdown-item>
                              <el-button type="text" @click="handleList(treeRoot,1)">查
看员工</el-button>
                            </el-dropdown-item>
                            <el-dropdown-item>
                              <el-button type="text" @click="handleDelete(data)">删除
</el-button>
                            </el-dropdown-item>
                          </el-dropdown-menu>
                        </el-dropdown>
                      </span>                  
                    </div>
                  </div>
                </el-tree>
(3) 构造数据
代码语言:javascript
复制
//数据绑定模型
data() {
    return {
      activeName: 'first',  //激活pane的名称
      dialogFormVisible:false,//是否显示弹出层标识
      parentId:'', //父id
      departData:{}, //部门列表
      formData:{} //表单提交数据
   }
 },
  //自定义方法
  methods: {
    getObject(params) {
      organList().then(res => {
        this.departData = res.data.data
     })
   }
 },
  //钩子函数
  created: function() {
    this.getObject()
 },

1.3.4 组织机构的增删改查

(1)新增部门

使用element-ui提供的dialog的弹出层构造弹出添加页面

代码语言:javascript
复制
<el-dialog title="编辑部门" :visible.sync="dialogFormVisible">
    <el-form ref="dataForm" :model="formData" label-width="120px">
        <el-form-item label="部门名称">
       <el-input v-model="formData.name" placeholder='请输入部门名称'></el-input>
        </el-form-item>
        <el-form-item label="部门编码">
       <el-input v-model="formData.code" placeholder='请输入部门编码'></el-input>
        </el-form-item>
        <el-form-item label="部门负责人">
       <el-input v-model="formData.manager" placeholder='请输入负责人'></el-input>
        </el-form-item>
        <el-form-item label="部门介绍">
       <el-input v-model="formData.introduce" placeholder='请输入介绍'></el-input>
        </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="createData">确定</el-button>
        <el-button @click="dialogFormVisible=false">取消</el-button>
    </div>
</el-dialog>

配置保存方法

代码语言:javascript
复制
  createData:function() {
      this.formData.parentId = this.parentId
      saveOrUpdate(this.formData)
       .then(res => {
     this.$message({message:res.data.message,type:res.data.success?"success":"error"});
          location.reload()
          this.dialogFormVisible=false
     })
   }
(2)修改部门
  1. 根据id查询部门
代码语言:javascript
复制
 handleEdit(id) {
      detail({id}).then( res=> {
        this.formData = res.data.data
        this.dialogFormVisible = true
        this.parentId = res.data.data.parentId
     })
   }
  1. 调用方法更新部门
(3)删除部门
代码语言:javascript
复制
  handleDelete(obj) {
      this.$confirm(
        `本次操作将删除${obj.name},删除后将不可恢复,您确认删除吗?`
     ).then(() => {
        remove({id:obj.id}).then( res=> {
         
this.$message({message:res.data.message,type:res.data.success?"success":"error"});
          location.reload()
       })
     })
   },

1.3.5 抽取组件

组件(Component) 是Vue.js 最强大的功能。可以通过将不同的业务拆分为不同的组件进行开发,让代码更加优雅提供可读性。当然页可以封装可重用的代码,通过传入对象的不同,实现组件的复用。

(1)抽取新增/修改页面到 /module-departments/components/add.vue
代码语言:javascript
复制
<template>
    <el-dialog title="编辑部门" :visible.sync="dialogFormVisible">
      <el-form ref="dataForm" :model="formData" label-width="120px">
        <el-form-item label="部门名称">
          <el-input v-model="formData.name" placeholder='请输入部门名称'></el-input>
        </el-form-item>
        <el-form-item label="部门编码">
          <el-input v-model="formData.code" placeholder='请输入部门编码'></el-input>
        </el-form-item>
        <el-form-item label="部门负责人">
          <el-input v-model="formData.manager" placeholder='请输入部门负责人'></el-input>
        </el-form-item> 
        <el-form-item label="部门介绍">
          <el-input v-model="formData.introduce" placeholder='请输入部门介绍'></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="createData">确定</el-button>
        <el-button @click="dialogFormVisible=false">取消</el-button>
      </div>
    </el-dialog>
</template>
<script>
import { saveOrUpdate } from '@/api/base/departments'
export default {
  name: 'dept-add',
  data() {
    return {
      dialogFormVisible:false,
      formData:{},
      parentId:''
   }
 },
 methods: {
    createData:function() {
      this.formData.parentId = this.parentId
      saveOrUpdate(this.formData)
       .then(res => {
         
this.$message({message:res.data.message,type:res.data.success?"success":"error"});
          location.reload()
          this.dialogFormVisible=false
     })
   }
 }
}</script>
(2) 在 /module-departments/page/index.vue 中引用组件
  • 导入组件
代码语言:javascript
复制
import deptAdd from './../components/add'  //导入组件
export default { 
	  //声明引用组件
	  components: { deptAdd }, //声明组件
	  data() {
	    return {
	      deptAdd: 'deptAdd', //配置组件别名
	      activeName: 'first', 
	      departData:{},
	   }
	 },
  }
  • 使用组件
代码语言:javascript
复制
//v-bind:is (绑定的组件名称)
//ref : 引用子组件中内容的别名
<component v-bind:is="deptAdd" ref="deptAdd"></component>
  • 改造新增修改方法
代码语言:javascript
复制
 handlAdd(parentId) {
      //对子组件中的属性复制
      this.$refs.deptAdd.formData = {};
      this.$refs.deptAdd.parentId = parentId
      this.$refs.deptAdd.dialogFormVisible = true;
   },
    handleEdit(id) {
      detail({id}).then( res=> {
        this.$refs.deptAdd.formData = res.data.data
        this.$refs.deptAdd.dialogFormVisible = true
        this.$refs.deptAdd.parentId = res.data.data.parentId
     })
   },
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 组织机构管理
    • 1.1 需求分析
      • 1.1.1 需求分析
      • 1.1.2 数据库表设计
    • 1.2 微服务实现
      • 1.2.1 抽取公共代码
      • 1.2.2 实现基本CRUD操作
    • 1.3 前端实现
      • 1.3.1 创建模块
      • 1.3.2 配置请求API
      • 1.3.3 构造列表
      • 1.3.4 组织机构的增删改查
      • 1.3.5 抽取组件
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档