diff --git a/docs/cs/os.md b/docs/cs/os.md index 2cc3292ff68e470da59354b5167bb8b661d71de6..0fc18a0d0d1d93384bf3677c15ce1eba90d9ab04 100644 --- a/docs/cs/os.md +++ b/docs/cs/os.md @@ -1,5 +1,8 @@ --- title: 计算机操作系统知识点大梳理 +shortTitle: 操作系统核心知识点 +description: 转载链接:https://mp.weixin.qq.com/s/G9ZqwEMxjrG5LbgYwM5ACQ +author: 月伴飞鱼 category: - 计算机基础 tag: diff --git a/docs/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni.md b/docs/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni.md new file mode 100644 index 0000000000000000000000000000000000000000..2398d65c1160bebc6845c827fed0183c730a14be --- /dev/null +++ b/docs/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni.md @@ -0,0 +1,742 @@ +--- +title: 还不会用mybatis-plus,手把手教你 +shortTitle: 还不会用mybatis-plus,手把手教你 +description: 转载链接:https://juejin.cn/post/7054726274362638350 +author: 我犟不过你 +category: + - 优质文章 +--- + +--- +theme: fancy +--- + +「这是我参与2022首次更文挑战的第2天,活动详情查看:[2022首次更文挑战](https://juejin.cn/post/7052884569032392740 \"https://juejin.cn/post/7052884569032392740\")」 + +本文主要介绍mybatis-plus这款插件,针对springboot用户。包括引入,配置,使用,以及扩展等常用的方面做一个汇总整理,尽量包含大家常用的场景内容。 + +>本文项目代码gitee地址: [https://gitee.com/wei_rong_xin/rob-necessities.git](https://gitee.com/wei_rong_xin/rob-necessities.git) + +关于mybatis-plus是什么,不多做介绍了,看官方文档:[https://baomidou.com/](https://baomidou.com/),咱们直接代码撸起来。 + +# 一、快速开始 + +本文基于springboot、maven、jdk1.8、mysql开发,所以开始前我们需要准备好这套环境。我的环境使用了nacos作为注册中心,不了解或需要搭建的参考:[nacos2.0搭建与配置](https://juejin.cn/post/7053977860612030477) + +新建如下数据库: + +![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6ebfb19ffe4446e3b3c2a2367e334206~tplv-k3u1fbpfcp-watermark.image?) + +建议大家选择utf8mb4这种字符集,做过微信的同学应该会知道,微信用户名称的表情,是需要这种字符集才能存储的。 + +我就默认其他环境已经准备好了,咱们直接从mybatis-plus开始。 + +## 1.1 依赖准备 +想要什么依赖版本的去maven仓库查看:[maven仓库地址](https://mvnrepository.com/) + +引入mybatis-plus依赖: +```xml + + com.baomidou + mybatis-plus-boot-starter + 3.5.0 + +``` +引入mysql依赖: +```xml + + mysql + mysql-connector-java + 8.0.27 + +``` + +目前,多数项目会有多数据源的要求,或者是主从部署的要求,所以我们还需要引入mybatis-plus关于多数据源的依赖: +```xml + + + com.baomidou + dynamic-datasource-spring-boot-starter + 3.5.0 + +``` +## 1.2 配置准备 + +springboot启动类。配置@MapperScan注解,用于扫描Mapper文件位置: +```java +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@EnableDiscoveryClient +@MapperScan(\"com.wjbgn.user.mapper\") +@SpringBootApplication +public class RobNecessitiesUserApplication { + + public static void main(String[] args) { + SpringApplication.run(RobNecessitiesUserApplication.class, args); + } + +} +``` + +数据源配置,此处配置一主一从的环境,当前我只有一台,所以此处配置一样的: +```yml +spring: + datasource: + dynamic: + primary: master #设置默认的数据源或者数据源组,默认值即为master + strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/rob_necessities?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai + username: root + password: 123456 + slave_1: + url: jdbc:mysql://127.0.0.1:3306/rob_necessities?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai + username: root + password: 123456 +``` +**补充**:这里面因为默认使用的是`HikariCP`数据源,目前也推荐使用这个,相比于`druid`有更高的性能,但是不能忽略下面的配置,否则服务会不断抛出异常,原因是数据库的连接时常和连接池的配置没有做好。 +```yml +spring: + datasource: + dynamic: + hikari: + max-lifetime: 1800000 + connection-timeout: 5000 + idle-timeout: 3600000 + max-pool-size: 12 + min-idle: 4 + connection-test-query: /**ping*/ +``` +## 1.3 启动服务 +下面直接启动服务: + + +![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c07b4dae7ff841c599977a29b0aaa8a6~tplv-k3u1fbpfcp-watermark.image?) + +得到如上结果表示启动成功了。 + +# 二、使用 + +前面我们成功的集成进来了mybatis-plus,配合springboot使用不要太方便。下面我们看看如何使用它来操作我们的数据库。介绍一下常规的用法。 + +## 2.1 实体类注解 + +mybatis-plus为使用者封装了很多的注解,方便我们使用,我们首先看下实体类中有哪些注解。有如下的实体类: +```java +@TableName(value = \"user\") +public class UserDO { + + /** + * 主键 + */ + @TableId(value = \"id\", type = IdType.AUTO) + private Long id; + + /** + * 昵称 + */ + @TableField(\"nickname\") + private String nickname; + + /** + * 真实姓名 + */ + private String realName; +} +``` + +* @TableName 表名注解,用于标识实体类对应的表。 + + 其说明如下,关于这些书写,常规情况基本很少用到,不做多余解释了: + ```java + @Documented + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) + public @interface TableName { + + /** + * 实体对应的表名 + */ + String value() default \"\"; + + /** + * schema + * + * @since 3.1.1 + */ + String schema() default \"\"; + + /** + * 是否保持使用全局的 tablePrefix 的值 + *

只生效于 既设置了全局的 tablePrefix 也设置了上面 {@link #value()} 的值

+ *
  • 如果是 false , 全局的 tablePrefix 不生效
  • + * + * @since 3.1.1 + */ + boolean keepGlobalPrefix() default false; + + /** + * 实体映射结果集, + * 只生效与 mp 自动注入的 method + */ + String resultMap() default \"\"; + + /** + * 是否自动构建 resultMap 并使用, + * 只生效与 mp 自动注入的 method, + * 如果设置 resultMap 则不会进行 resultMap 的自动构建并注入, + * 只适合个别字段 设置了 typeHandler 或 jdbcType 的情况 + * + * @since 3.1.2 + */ + boolean autoResultMap() default false; + + /** + * 需要排除的属性名 + * + * @since 3.3.1 + */ + String[] excludeProperty() default {}; + } + ``` + +* @TableId 主键注解 + + 看看其源码: + ```java + @Documented + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) + public @interface TableId { + + /** + * 字段值(驼峰命名方式,该值可无) + */ + String value() default \"\"; + + /** + * 主键ID + * {@link IdType} + */ + IdType type() default IdType.NONE; + } + ``` + 其中IdType很重要: + + + | 名称 | 描述 | + | --- | --- | + | AUTO | 数据库自增ID | + | NONE | 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) + | INPUT | 用户自己设置的ID | + | ASSIGN_ID | 当用户传入为空时,自动分配类型为Number或String的主键(雪花算法) | + | ASSIGN_UUID | 当用户传入为空时,自动分配类型为String的主键 | + +* @TableFiled 表字段标识 + + 下面看看其主要常用属性: + + | 名称 | 描述 | + | --- | --- | + | value | 数据库字段名 | + | condition | 字段 `where` 实体查询比较条件,通过`SqlCondition`设置
    如果未设置条件,则按照正常相等来查询
    若设置则按照以下规则:
    等于:EQUAL = \"%s=#{%s}\";
    不等于:NOT_EQUAL = \"%s<>#{%s}\";
    左右模糊:LIKE = \"%s LIKE CONCAT('%%',#{%s},'%%')\";
    oracle左右模糊ORACLE_LIKE = \"%s LIKE CONCAT(CONCAT('%%',#{%s}),'%%')\";
    左模糊:LIKE_LEFT = \"%s LIKE CONCAT('%%',#{%s})\";
    右模糊:LIKE_RIGHT = \"%s LIKE CONCAT(#{%s},'%%')\";| + | fill | 自动填充策略,通过`FieldFill`设置
    不处理:FieldFill.DEFAULT
    插入时填充字段:FieldFill.INSERT
    更新时填充字段:FieldFill.UPDATE
    插入或新增时填充字段:FieldFill.INSERT_UPDATE | + + 关于其他的属性,我不太推荐使用,用得越多,越容易蒙圈。可以通过wapper查询去设置。 + +## 2.2 CRUD +mybatis-plus封装好了一条接口供我们直接调用。关于内部的具体方法,在使用时候自己体会吧,此处不列举了。 + +### 2.2.1 Service层CRUD +我们使用的时候,需要在自己定义的service接口当中继承`IService`接口: +```java +import com.baomidou.mybatisplus.extension.service.IService; +import com.wjbgn.user.entity.UserDO; + +/** + * @description: 用户服务接口 + * @author:weirx + * @date:2022/1/17 15:02 + * @version:3.0 + */ +public interface IUserService extends IService { +} +``` +同时要在我们的接口实现impl当中继承`ServiceImpl`,实现自己的接口: +```java +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.wjbgn.user.entity.UserDO; +import com.wjbgn.user.mapper.UserMapper; +import com.wjbgn.user.service.IUserService; + +/** + * @description: 用户接口实现 + * @author:weirx + * @date:2022/1/17 15:03 + * @version:3.0 + */ +public class UserServiceImpl extends ServiceImpl implements IUserService { + +} +``` + +所以我们的 +### 2.2.2 Mapper层CRUD +mybatis-plus将常用的CRUD接口封装成了`BaseMapper`接口,我们只需要在自己的Mapper中继承它就可以了: +```java +/** + * @description: 用户mapper + * @author:weirx + * @date:2022/1/17 14:55 + * @version:3.0 + */ +@Mapper +public interface UserMapper extends BaseMapper { +} +``` + +## 2.3 分页 +使用分页话需要增加分页插件的配置: +```java +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@MapperScan(\"com.wjbgn.*.mapper*\") +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } + +} +``` +如上配置后,我们直接使用分页方法就行。 + +## 2.4 逻辑删除配置 +很多情况下我们的系统都需要逻辑删除,方便恢复查找误删除的数据。 + +通过mybatis-plus可以通过全局配置的方式,而不需要再去手动处理。针对更新和查询操作有效,新增不做限制。 + +通常以我的习惯逻辑删除字段通常定义为`is_delete`,在实体类当中就是`isDelete`。那么在配置文件中就可以有如下的配置: +```yml +mybatis-plus: + global-config: + db-config: + logic-delete-field: isDelete # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) +``` + +或者通过注解`@TableLogic` +```java +@TableLogic +private Integer isDelete; +``` + +## 2.5 通用枚举配置 +相信后端的同学都经历过一个情况,比如`性别`这个字段,分别值和名称对应`1男`、`2女`,这个字段在数据库时是数值类型,而前端展示则是展示字符串的名称。有几种常见实现方案呢? +* 数据库查询sql通过case判断,返回名称,以前oracle经常这么做 +* 数据库返回的值,重新遍历赋值进去,这时候还需要判断这个值到底是男是女。 +* 前端写死,返回1就是男,返回2就是女。 + +相信无论哪种方法都有其缺点,所以我们可以使用mybatis-plus提供的方式。我们在返回给前端时: +* 只需要在遍历时get这个枚举,直接赋值其名称,不需要再次判断。 +* 直接返回给前端,让前端去去枚举的name + +这样大家都不需要写死这个值。 + +下面看看如何实现这个功能: +* 兴义枚举,实现IEnum接口: + + ```java + import com.baomidou.mybatisplus.annotation.IEnum; + import com.fasterxml.jackson.annotation.JsonFormat; + + /** + * @description: 性别枚举 + * @author:weirx + * @date:2022/1/17 16:26 + * @version:3.0 + */ + @JsonFormat(shape = JsonFormat.Shape.OBJECT) + public enum SexEnum implements IEnum { + MAN(1, \"男\"), + WOMAN(2, \"女\"); + private Integer code; + private String name; + + SexEnum(Integer code, String name) { + this.code = code; + this.name = name; + } + + @Override + public Integer getValue() { + return code; + } + + public String getName() { + return name; + } + + } + ``` + @JsonFormat注解为了解决枚举类返回前端只展示构造器名称的问题。 + +* 实体类性别字段 + ```java + @TableName(value = \"user\") + public class UserDO { + + /** + * 主键 + */ + @TableId(value = \"id\", type = IdType.AUTO) + private Long id; + + /** + * 昵称 + */ + @TableField(value = \"nickname\",condition = SqlCondition.EQUAL) + private String nickname; + + /** + * 性别 + */ + @TableField(value = \"sex\") + private SexEnum sex; + + /** + * 版本 + */ + @TableField(value = \"version\",update = \"%s+1\") + private Integer version; + + /** + * 时间字段,自动添加 + */ + @TableField(value = \"create_time\",fill = FieldFill.INSERT) + private LocalDateTime createTime; + } + ``` +* 配置文件扫描枚举 + ```yml + mybatis-plus: + # 支持统配符 * 或者 ; 分割 + typeEnumsPackage: com.wjbgn.*.enums + ``` + +* 定义配置文件 + ```java + @Bean + public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() { + return properties -> { + GlobalConfig globalConfig = properties.getGlobalConfig(); + globalConfig.setBanner(false); + MybatisConfiguration configuration = new MybatisConfiguration(); + configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class); + properties.setConfiguration(configuration); + }; + } + ``` + +* 序列化枚举值为数据库值 + + 以下我是使用的fastjson: + + * 全局(添加在前面的配置文件中): + + ```java + @Bean + public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() { + // 序列化枚举值为数据库存储值 + FastJsonConfig config = new FastJsonConfig(); + config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString); + + return properties -> { + GlobalConfig globalConfig = properties.getGlobalConfig(); + globalConfig.setBanner(false); + MybatisConfiguration configuration = new MybatisConfiguration(); + configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class); + properties.setConfiguration(configuration); + }; + } + ``` + * 局部 + + ```java + @JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString) + private SexEnum sex; + ``` + +## 2.6 自动填充 + +还记得前面提到的实体类当中的注解`@TableFeild`吗?当中有个属性叫做fill,通过`FieldFill`设置属性,这个就是做自动填充用的。 + +```java +public enum FieldFill { + /** + * 默认不处理 + */ + DEFAULT, + /** + * 插入填充字段 + */ + INSERT, + /** + * 更新填充字段 + */ + UPDATE, + /** + * 插入和更新填充字段 + */ + INSERT_UPDATE +} +``` + +但是这个直接是不能使用的,需要通过实现mybatis-plus提供的接口,增加如下配置: + +```java +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * description: 启动自动填充功能 + + * @return: + * @author: weirx + * @time: 2022/1/17 17:00 + */ +@Component +public class MyMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + // 起始版本 3.3.0(推荐使用) + this.strictInsertFill(metaObject, \"createTime\", LocalDateTime.class, LocalDateTime.now()); + } + + @Override + public void updateFill(MetaObject metaObject) { + // 起始版本 3.3.0(推荐) + this.strictUpdateFill(metaObject, \"updateTime\", LocalDateTime.class, LocalDateTime.now()); + } +} +``` +字段如下: +```java +/** + * 时间字段,自动添加 + */ +@TableField(value = \"create_time\",fill = FieldFill.INSERT) +private LocalDateTime createTime; +``` + +## 2.7 多数据源 +前面提到过,配置文件当中配置了主从的方式,其实mybatis-plus还支持更多的方式: + +* 多主多从 +```yaml +spring: + datasource: + dynamic: + primary: master #设置默认的数据源或者数据源组,默认值即为master + strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 + datasource: + master_1: + master_2: + slave_1: + slave_2: + slave_3: +``` +* 多种数据库 +```yaml +spring: + datasource: + dynamic: + primary: mysql #设置默认的数据源或者数据源组,默认值即为master + strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 + datasource: + mysql: + oracle: + postgresql: + h2: + sqlserver: +``` + +* 混合配置 +```yaml +spring: + datasource: + dynamic: + primary: master #设置默认的数据源或者数据源组,默认值即为master + strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 + datasource: + master_1: + slave_1: + slave_2: + oracle_1: + oracle_2: +``` + +上面的三种方式,除了混合配置,我觉得都有肯能出现的吧。 + +* @DS注解 + +可以注解在方法上或类上,**同时存在就近原则 【方法上注解】 优先于 【类上注解】**: + +```java +@DS(\"slave_1\") +public class UserServiceImpl extends ServiceImpl implements IUserService { + + + @DS(\"salve_1\") + @Override + public List getList() { + return this.getList(); + } + + @DS(\"master\") + @Override + public int saveUser(UserDO userDO) { + boolean save = this.save(userDO); + if (save){ + return 1; + }else{ + return 0; + } + } +} +``` + +# 三、测试 +经过上面的配置,下面开始进入测试验证阶段。 + +建立一张表: +```sql +CREATE TABLE `user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `nickname` varchar(255) NOT NULL COMMENT '昵称', + `sex` tinyint(1) NOT NULL COMMENT '性别,1男2女', + `create_time` datetime NOT NULL COMMENT '创建时间', + `is_delete` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除 1是,0否', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8mb4; +``` + +controller: + +```java +/** + * @description: 用户controller + * @author:weirx + * @date:2022/1/17 17:39 + * @version:3.0 + */ +@RestController +@RequestMapping(\"/user\") +public class UserController { + + @Autowired + private IUserService userService; + + /** + * description: 新增 + + * @return: boolean + * @author: weirx + * @time: 2022/1/17 19:11 + */ + @RequestMapping(\"/save\") + public boolean save() { + UserDO userDO = new UserDO(); + userDO.setNickname(\"大漂亮\"); + userDO.setSex(SexEnum.MAN); + + return userService.save(userDO); + } + + /** + * description: 修改 + * @param nickname + * @param id + * @return: boolean + * @author: weirx + * @time: 2022/1/17 19:11 + */ + @RequestMapping(\"/update\") + public boolean update(@RequestParam String nickname,@RequestParam Long id) { + UserDO userDO = new UserDO(); + userDO.setNickname(nickname); + userDO.setId(id); + return userService.updateById(userDO); + } + + /** + * description: 删除 + * @param id + * @return: boolean + * @author: weirx + * @time: 2022/1/17 19:11 + */ + @RequestMapping(\"/delete\") + public boolean delete(@RequestParam Long id) { + UserDO userDO = new UserDO(); + userDO.setId(id); + return userService.removeById(userDO); + } + + /** + * description: 列表 + * @return: java.util.List + * @author: weirx + * @time: 2022/1/17 19:11 + */ + @RequestMapping(\"/list\") + public List list() { + return userService.list(); + } + + /** + * description: 分页列表 + * @param current + * @param size + * @return: com.baomidou.mybatisplus.extension.plugins.pagination.Page + * @author: weirx + * @time: 2022/1/17 19:11 + */ + @RequestMapping(\"/page\") + public Page page(@RequestParam int current,@RequestParam int size) { + return userService.page(new Page<>(current,size), new QueryWrapper(new UserDO())); + } + +} +``` + +记过上面的接口验证,功能没有问题,集成成功。 + + +>上文基本覆盖了mybatis-plus常用的特性,有同学说自动代码生成没说?后面会有专门的章节去讲解` +>mybatis-plus-generator` + `velocity`整合的文章,从后台代码,到前端代码全部一键生成。 + + +>本文项目代码gitee地址: [https://gitee.com/wei_rong_xin/rob-necessities.git](https://gitee.com/wei_rong_xin/rob-necessities.git) + + + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2196a876b9554168ae9586ebf6f50b6e~tplv-k3u1fbpfcp-watermark.image?) diff --git a/docs/nice-article/jiagoushizenyanghuizhijitongjiagoulantu.md b/docs/nice-article/jiagoushizenyanghuizhijitongjiagoulantu.md new file mode 100644 index 0000000000000000000000000000000000000000..3ee7d3b0f26aef4015b2aa4c1db4388d7974ed08 --- /dev/null +++ b/docs/nice-article/jiagoushizenyanghuizhijitongjiagoulantu.md @@ -0,0 +1,171 @@ +--- +author: JAVA日知录 +title: 架构师怎样绘制系统架构蓝图? +category: + - 优质文章 +--- +前言 +-- + +大家好,我是飘渺。 + +今天我们来了解一些关于软件设计文档的基础知识,这样你在学习后面的具体案例时,就能更加清楚地理解文档是基于什么方式来组织的了。 + +首先,请你设想这样一个场景:如果公司安排你做架构师,要你在项目开发前期进行软件架构设计,你该如何开展你的工作?如何输出你的工作成果?如何确定你的设计是否满足用户需求?你是否有把握最后交付的软件是满足要求的?是否有把握让团队每个工程师清楚自己的职责范围并有效地完成开发工作…… + +这些问题其实都是软件开发管理与技术架构的核心诉求,而架构师的核心工作就是做好软件设计,解决这些诉求。这些问题搞定了,软件的开发过程和结果也就都得到了保证。那怎么实现这些诉求呢?我们主要的手段就是软件建模,以及将这些软件模型组织成一篇有价值的软件设计文档。 + +软件建模 +---- + +所谓软件建模,就是为要开发的软件建造模型。 + +模型是对客观存在的抽象,例如著名的物理学公式 E=mc2,就是质量能量转换的物理规律的数学模型。除了物理学公式以外,还有一些东西也是模型,比如地图是对地理空间的建模;机械装置、电子电路、建筑设计的各种图纸是对物理实体的建模。而软件,也可以通过各种图进行建模。 + +软件系统庞大复杂,通过软件建模,我们可以抽象软件系统的主要特征和组成部分,梳理这些关键组成部分的关系。在软件开发过程中依照模型的约束开发,系统整体的格局和关系就会可控。相关人员从始至终都能清晰了解软件的蓝图和当前的进展,不同的开发工程师会清晰自己开发的模块和其他同事工作内容的关系与依赖,并按照这些模型开发代码。 + +那么我们是根据什么进行软件建模的呢?要解答这个疑问,你需要先知道,在软件开发中,有两个客观存在。 + +一个是我们要解决的领域问题。比如我们要开发一个电子商务网站,那么客观的领域问题就是如何做生意,卖家如何管理商品、管理订单、服务用户,买家如何挑选商品,如何下订单,如何支付等等。对这些客观领域问题的抽象就是各种功能及其关系、各种模型对象及其关系、各种业务处理流程。 + +另一个客观存在就是最终开发出来的软件系统。软件系统要解决的问题包括软件由哪些主要类组成,这些类如何组织构成一个个的组件,这些类和组件之间的依赖关系如何,运行期如何调用,需要部署多少台服务器,服务器之间如何通信等。 + +而对这两个客观存在进行抽象化处理的手段,就是我们的软件模型。 + +![](https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4ia9Fva7RU38ovpD2mCYXIm1UVdmia0792Oeicm4HEfvaHPvbfyWgbmP8fzxf36tnKWLKwaeiaCmBwCBg/640?wx_fmt=png) + +一方面我们要对领域问题和要设计的软件系统进行分析、设计、抽象,另一方面,我们根据抽象出来的模型进行开发,最终实现出一个软件系统,这就是软件开发的主要过程。而对领域问题和软件系统进行分析、设计和抽象的这个过程,就是软件建模设计。 + +软件设计方法 +------ + +因此,软件设计其实就是软件建模的过程。我们通过软件建模工具,将软件模型画出来,实现软件设计。 + +在实践中,通常用来进行软件建模画图的工具是 UML,统一建模语言。UML 包含的软件模型有 10 种,其中常用的有 7 种:类图、序列图、组件图、部署图、用例图、状态图和活动图。 + +下面我们简单了解下这 7 种常用 UML 图的使用场景和基本样例。在专栏后面的设计文档中,你会多次见到它们,看多了,你就懂了,也就自然会画了。当然,如果你想更详细地学习 UML 知识,我也非常鼓励,并且推荐你阅读马丁富勒的《UML 精粹》一书。 + +### 类图 + +类图是最常见的 UML 图形,用来描述类的特性和类之间的静态关系。 + +一个类包含三个部分:类的名字、类的属性列表和类的方法列表。类之间有 6 种静态关系:关联、依赖、组合、聚合、继承、泛化。把相关的一组类及其关系用一张图画出来,就是类图。 + +比如你在后面的课程中会遇到下面这幅图,它就是类图。你可以把我上面说的类图包含元素和图片一一对照,感受类图的用法。 + +![](https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4ia9Fva7RU38ovpD2mCYXIm1AUopK9QQDr6pV5WBp5Nkp4JQRrZKBY4PkicgCj6kg4feC1wcW6U5mQw/640?wx_fmt=png) + + + +### 时序图 + +类图之外,另一种常用的图是时序图,类图描述类之间的静态关系,时序图则用来描述参与者之间的动态调用关系。 + +![](https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4ia9Fva7RU38ovpD2mCYXIm1EcAr3PticUpWHHw7DAczyFaHZVu2UXy8XQ3haOgqqzgYPXFDvfGMahA/640?wx_fmt=png) + + + +### 组件图 + +组件是比类粒度更大的设计元素,一个组件中通常包含很多个类。组件图有的时候和包图的用途比较接近,组件图通常用来描述物理上的组件,比如一个 JAR、一个 DLL 等等。在实践中,我们进行模块设计的时候,用得更多的就是组件图。 + +![](https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4ia9Fva7RU38ovpD2mCYXIm1ge6DR9VNudpF79ic5ACNSaWfbc3MeL53PyYuxIwvibPoGicpibQsOXFZibQ/640?wx_fmt=png) + + + +组件图描述组件之间的静态关系,主要是依赖关系,如果你想要描述组件之间的动态调用关系,可以使用组件时序图,以组件作为参与者,描述组件之间的消息调用关系。 + +### 部署图 + +部署图描述软件系统的最终部署情况,比如需要部署多少服务器,关键组件都部署在哪些服务器上。 + +![](https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4ia9Fva7RU38ovpD2mCYXIm1Nj686l5iat4aaxb22dzibKUNNdn4fncgRnIjgicia3klrFmJUb4mh2yY2A/640?wx_fmt=png) + +部署图是软件系统最终物理呈现的蓝图,根据部署图,所有相关者,诸如客户、老板、工程师都能清晰地了解到最终运行的系统在物理上是什么样子,和现有的系统服务器的关系,和第三方服务器的关系。根据部署图,还可以估算服务器和第三方软件的采购成本。 + +因此部署图是整个软件设计模型中,比较宏观的一种图,是在设计早期就需要画的一种模型图。根据部署图,各方可以讨论对这个方案是否认可。只有对部署图达成共识,才能继续后面的细节设计。 + +### 用例图 + +用例图通过反映用户和软件系统的交互,描述系统的功能需求。 + +![](https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4ia9Fva7RU38ovpD2mCYXIm1cDQtmQibY0JasQqXzmH79ib1pj4WziajST03YL4yiaibKicVwy0ooono02lw/640?wx_fmt=png) + +图中小人形象的元素,被称为角色,角色可以是人,也可以是其他的系统。系统的功能可能会很复杂,所以一张用例图可能只包含其中一小部分功能,这些功能被一个矩形框框起来,这个矩形框被称为用例的边界。框里的椭圆表示一个一个的功能,功能之间可以调用依赖,也可以进行功能扩展。 + +### 状态图 + +状态图用来展示单个对象生命周期的状态变迁。 + +业务系统中,很多重要的领域对象都有比较复杂的状态变迁,比如账号,有创建状态、激活状态、冻结状态、欠费状态等等各种状态。此外,用户、订单、商品、红包这些常见的领域模型都有多种状态。 + +这些状态的变迁描述可以在用例图中用文字描述,随着角色的各种操作而改变,但是用这种方式描述,状态散乱在各处,不要说开发的时候容易搞错,就是产品经理自己在设计的时候,也容易搞错对象的状态变迁。 + +UML 的状态图可以很好地解决这一问题,一张状态图描述一个对象生命周期的各种状态,及其变迁的关系。如图所示,门的状态有开 Opened、关 Closed 和锁 Locked 三种,状态与变迁关系用一张状态图就可以搞定。 + +![](https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4ia9Fva7RU38ovpD2mCYXIm19rM7gVEzyufk01L7xM6eKPybnYcUIEp1ndQg5GKB0GboU0iaVefb5Mg/640?wx_fmt=png) + +### 活动图 + +活动图主要用来描述过程逻辑和业务流程。UML 中没有流程图,很多时候,人们用活动图代替流程图。 + +![](https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4ia9Fva7RU38ovpD2mCYXIm1QdyA2cGvyDJwEAMIQicx3aS4zGafmtBFz9b7hgU6DYjj4FBUwQvxYWA/640?wx_fmt=png) + +活动图和早期流程图的图形元素也很接近,实心圆代表流程开始,空心圆代表流程结束,圆角矩形表示活动,菱形表示分支判断。 + +此外,活动图引入了一个重要的概念——泳道。活动图可以根据活动的范围,将活动根据领域、系统和角色等划分到不同的泳道中,使流程边界更加清晰。 + +我们上面介绍了 UML 建模常用的 7 种模型,那么这 7 种模型分别应用在软件设计的什么阶段?用来表达什么样的设计意图呢? + +软件文档设计 +------ + +软件设计文档就是架构师的主要工作成果,它需要阐释本文开头提到的各种诉求,描绘软件的完整蓝图,而软件设计文档的主要组成部分就是软件模型。 + +软件设计过程可以拆分成需求分析、概要设计和详细设计三个阶段。 + +在需求分析阶段,主要是通过用例图来描述系统的功能与使用场景;对于关键的业务流程,可以通过活动图描述;如果在需求阶段就提出要和现有的某些子系统整合,那么可以通过时序图描述新系统和原来的子系统的调用关系;可以通过简化的类图进行领域模型抽象,并描述核心领域对象之间的关系;如果某些对象内部会有复杂的状态变化,比如用户、订单这些,可以用状态图进行描述。 + +在概要设计阶段,通过部署图描述系统最终的物理蓝图;通过组件图以及组件时序图设计软件主要模块及其关系;还可以通过组件活动图描述组件间的流程逻辑。 + +在详细设计阶段,主要输出的就是类图和类的时序图,指导最终的代码开发,如果某个类方法内部有比较复杂的逻辑,那么可以将这个方法的逻辑用活动图进行描述。 + +我们在每个设计阶段使用几种 UML 模型对领域或者系统进行建模,然后将这些模型配上必要的文字说明写入到文档中,就可以构成一篇软件设计文档了。 + +我们专栏中的十几讲软件设计案例,都是按照这样的方式组织的,你可以在学习的过程中,一方面了解各种系统软件是如何设计的,一方面也可以借鉴设计文档是如何写作的。 + +同时也要说明一下,设计文档的写法并没有一定之规,最重要的是这个文档能否向阅读者传递出架构师完整的设计意图。而不同的阅读者关注点是不同的,老板、客户、运维、测试、开发这些角色都是设计文档的阅读者,他们想要看到的东西显然是不一样的。 + +客户和测试人员可能更关注功能性需求和实现逻辑,老板和运维人员可能更关注非功能需求和整体架构,而开发人员可能更关注整体架构与关键技术细节。 + +我们专栏的案例基本上是以开发人员作为阅读视角进行编写的,你在阅读这些案例时,会明显感觉到我的表达方式和其他专栏文章不太一样,措辞会更“坚硬”一点,文字和读者的距离也有点“疏离”,而这正是设计文档自身的特质。 + +架构、系统,文档、相关人员之间的关系可以参考下面这张图。 + +![](https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4ia9Fva7RU38ovpD2mCYXIm1M2PSqxlcstZxURH7ymkun4ebusbBh8B5ribqLqgboUXH9dy084U3Ljw/640?wx_fmt=png) + +每个软件系统都需要有一个架构,每个架构都包含若干架构元素。架构元素就是前面提到的服务器、组件、类、消息、用例、状态等等。这些元素之间的关系是什么?如何把它们组织在一起?我们可以用部署图、组件图、时序图等各种模型图来描述。 + +架构最终需要一个文档来承载,把这些模型图放进这个文档,再配以适当的文字说明,就是一篇架构设计文档。而设计文档是给人阅读的,这些人就是系统的相关方。不同的相关方关注点不同,也需要由不同的模型图来进行表达,所以架构师应该针对不同的相关方,使用不同的模型图输出不同的架构文档。 + +小结 +-- + +软件设计就是在软件开发之前,对要解决的业务问题和对要实现的软件系统进行思考,并将这个思考的结果通过软件模型表达出来的过程。 + +人类作为万物之灵,最大的特点就是,在行动之前就已经在头脑中将行动的过程和行动的结果构建成了一个蓝图,然后将这个蓝图付诸实践。我们的祖先将第一块石头打磨成石器的时候,就已经拥有了这种能力。软件系统的开发是一个复杂的智力活动,参与其中的我们更需要拥有构建蓝图并付诸实践的能力。 + +目前有个很火的词叫“元宇宙”,“元”通俗地讲,就是一切开始的地方,是关于如何用自己描述自己,是抽象之上的抽象。这种“元”能力对架构师而言,非常重要。架构师只有掌握各种技术背后的技术,了解各种问题背后的问题,才能超越当下的种种羁绊,设计出面向未来的架构。 + +好了,今天就到这儿吧,我是飘渺,我们下期见~~ + +**转自:****https://time.geekbang.org/column/article/486761** + +* * * + +最后说一句(别白嫖,求关注) +-------------- + +新开了一个纯技术交流群(一群已满),群里氛围还不错,无广告,无套路,单纯的吹牛逼,侃人生,想进的可以通过下方二维码加我微信,备注进群! + +![](https://mmbiz.qpic.cn/mmbiz_jpg/PxMzT0Oibf4iaaq1LHN5nmBoW0HpH70QAzKz7kqcXajmMbhLkK7rc6CRLcKhybrXOkejBIMwTr56xxbGiameeNPEg/640?wx_fmt=jpeg) \ No newline at end of file diff --git a/docs/nice-article/jielehenduosihuodiganchu.md b/docs/nice-article/jielehenduosihuodiganchu.md new file mode 100644 index 0000000000000000000000000000000000000000..900070e110d7cc47499f744f67ace84ec4607c1e --- /dev/null +++ b/docs/nice-article/jielehenduosihuodiganchu.md @@ -0,0 +1,60 @@ +--- +title: 接了很多私活的感触 +shortTitle: 接了很多私活的感触 +description: 转载链接:https://juejin.cn/post/7088538894891188238 +author: 前端要努力 +category: + - 优质文章 +--- + +--- +theme: awesome-green +--- +## 接私活 + - 今年搞了一个团队接私活,现在有20多人,我这边主要负责商务洽谈,合同签署,进度,风险把控,结果交付之类的活,不涉及到具体代码的编写,交付了好几个项目,今天来谈一谈私活的一些经历 + +## 私活的种类 + - 总类就太多了,后台管理,跨境电商,股票交易,小程序,APP,等等 + - 有总包或者单包一说,总包就是前后端,服务器都包,算总价;单包就是,你只做前端或者后端,对于你一个人的话,基本上是单包的比较多,总包的话,小项目可以,大项目你一个人就吃不住了,交付风险巨大 +## 渠道 + - 各种众包的网站,但是我看上面报价太低了,感觉很不靠谱,没有接过 + - 主要是朋友,渠道介绍 + - 还有的同学接国外的单,但是吃英语,英语不好没法沟通 + +## 如何谈价格 + - 接过私活的同学都知道,你人在上海,和人在西安的人天价格都是不一样的,而且每个人的工资都是不一样的 + - 首先一点要求你的经验丰富些,你第一次不会,你可以找一些资深的技术帮你去做估时间,大概要多少人天,要按照你自己的水平做一个合理的排期,然后才好谈价格 + - 比方你现在工资30000一个月然后30000\u002F21.75 = 1379 ,一般接私活的价格是你的日薪水的1.5 倍,如果低于2000人天你是不划算的,还不如去公司加班,因为你做私活是面向结果交付的,没办法摸鱼!所以你要怎么能够达到你日薪2K的收入,这就很有讲究!你报价2k\u002F人天,不用想你肯定会被pass的! + + - 一般的价格500-1000\u002F人天,太高了甲方一做对比就不会让你干了,货比三家吗,所以这个时候就很有讲究了,也就是套路来了,单价低,你就把时间干长,但是也不能太长,这时候就是沟通的艺术了,如何达到自己的目的,然后让甲方满意,你自己也满意,如果都不满意这个活也就没有继续干下去的必要了, +## 收入 + - 收入多少看项目,有的小项目几K,有的几万,几十万,还有的同学能够接住几百万的项目,大家不用觉得吃惊,单价10万+的项目非常多,但是资源只掌握在少部分人的手里,你干一个大项目就够你吃好多年了,上个月一个朋友说他们团队接了一个总包近500W的大项目,说实话酸的很,为何不是我接的,接到手立马辞职,哈哈!透露一点,在某些领域,非常吃关系,有资源的同学,动动嘴钱就到手了,你看他啥也没干,6位数或者更高的收益就到手了,大家不要喷我,我是经历过,懂的都懂; + - 对于我个人,我不写代码,我负责管理,风险把控,我拿10%-25%,看项目的大小!目前接过最大的项目过6位数,所以我在这块收入也是不稳定的,随缘,这个月没接到项目一分没有,多了就不好说了!看运气!一般低于一万的项目我是不接的,直接转手给被人做,没的赚头! +## 风险 + - 交付风险:这个钱你能不能挣,能不能按期,按质量的交付,别钱没赚到,项目烂尾,还搞一肚子气 + - 一手交钱,一手交代码,这是规矩,有的同学不懂,把代码给甲方,尾款就凉了,找谁说理去! + - 比方项目排期为5个迭代,加上定金和尾款应该就是7个迭代,按照迭代给钱,那钱的交付就是定金:20%-30%,尾款:这个控制的低一点,因为很多不靠谱的甲方会压你的尾款,大概10%,其他的就是迭代内平均分,每个迭代交付结束之后付钱 + - 不要一起交付再给钱,小项目可以,大项目一定要分期结算,不然遇到不靠谱的人,不给你钱,你也很无奈! + - 有的时候别人会让你给发票之类的,你要是个人的,花点钱找别人搞,税点要不了多少钱! + - 做事情做重要的是诚信,对于一些不靠谱的甲方,直接pass掉,长痛不如短痛,接了之后钱挣不了多少,搞的一肚子火! + - 反正这个钱不好挣,会搞的你非常忙,你要是写业务,有些时候遇到不靠谱的会让你很难受 + - 不要影响到你的本职工作!!!这个很重要,反正我在工作时间内不会去花时间搞这些玩意,主业还是要做好,私活是不稳定的! +## 时间 === 金钱 vs 时间\u003E\u003E\u003E金钱 +- 思考下,你是把你的时间花在接私活上面,还是花在提升个人能力上面 +- 据我了解,对大部分人来说做私活一个月能做到1w以上的人基本寥寥无几,你如果手握资源,那随意搞钱 +- 我虽然带了很多人接私活做,我只是整合资源,赚个提成;但是我还是不建议大家把宝押在接私活上面,因为我觉得这个本来就是不稳定的,一定要把绝大部分的时间压在提升个人能力上面,有人会说接私活写项目,不是也在提升自己的个人能力吗!我觉得不是的,你接私活做很多项目,只能说你写代码比较熟练,你是面向结果快速交付的,只要甲方觉得没问题,你不会去考虑项目性能,优化之类的,角度站高一点,你做的事情太杂,对于你个人成长的价值不是很大!凡事都有但是,哈哈,万一你有机会接到500w的项目呢!这时候不要想了,赶紧接下来,哈哈! + +- 大家加油!干啥都行,心里觉得平衡就可以,主要还是看你自己的抉择!但是一定要把主业工作干好,干漂亮! +## 传送门 + - [铜三铁四下,拿到前端技术专家的offer](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F7086381206236823559) + - [卷王的2021总结](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F7036689189567463432) + - [六点打卡交流群](https:\u002F\u002Fjuejin.cn\u002Fuser\u002F1943592288395479\u002Fpins) + - [我为什么喜欢手抄代码](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F7007243669921071117) + - [前端三年:小白变成老油条](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F7016189093851693069) + - [今年面试了100+的前端同学我的总结](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F7008027646898487326) + - [我为什么坚持六点起床](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F7000659708948250660#comment) + - [我读技术书很焦虑,读不下去书怎么办?](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F7004611718009585678) + - [勤奋努力 === \"卷\" ?](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F7002030597581307941) + - [vite + react + ts 手摸手做项目系列一 (项目配置篇)](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F6991728251034959885) + - [vite + react + ts 手摸手做项目系列二 (实战篇)](https:\u002F\u002Fjuejin.cn\u002Fpost\u002F6997560918418653198) + diff --git a/docs/nice-article/jishuzongjianbeikaichuliao.md b/docs/nice-article/jishuzongjianbeikaichuliao.md new file mode 100644 index 0000000000000000000000000000000000000000..650147478e4d019b1cca99f67d9ad3dfb4529185 --- /dev/null +++ b/docs/nice-article/jishuzongjianbeikaichuliao.md @@ -0,0 +1,204 @@ +--- +author: findyi +title: 技术总监被开除了.... +category: + - 优质文章 +--- + +阅读本文大概需要6分钟。 + +昨天某nft公司公众号的一篇文章刷屏了,几小时就获得了几十万阅读。 + + + +这篇文章是这家公司的前任技术负责人写的,看得出来这哥们怨念很深: + + + +![](https://mmbiz.qpic.cn/mmbiz_jpg/ibDBKEpdfwEBq1EjG5gX3tVbz2WwJjKxAL7OZ6rRz6B1lwonvGU8cCeEu9OykTqEk4iarAriccO5FuQwBA5xb0CMQ/640?wx_fmt=jpeg) + + + +40天赶出一个app但老板不满意,老板认为20天足够了,其实做过原生app开发的同学应该知道,这个速度还算比较快了。 + + + +并且火急火燎的上线导致bug不少,老板的应对方式不是谅解,而是开启无限攻击模式,最终把这个技术负责人和技术团队都给开了。 + + + +看完后感触良多,洋哥曾经也做过A/B轮公司CTO,也遇到过很多糟心的事情,一句话总结:技术负责人在不懂技术且不尊重技术的老板那,就是个夜壶。夜壶是什么?急的时候就是个宝贝,不急了就一脚踢开。 + + + +这是很多技术负责人的现状和无奈,经常也有技术总监朋友问洋哥诸如这样的问题:怎么样让老板知道开发时间不能再短了? + + + +怎么样让老板理解技术人员薪资是比运营高的?怎么样让老板知道一天超过12个小时真的效率很低了? + + + +讲真并没有什么办法,老板并不是不懂,而是故意装不懂。 + + + +最近甚至有一个“新物种”:“中华田园敏捷开发” + + + +这玩意尤其在土老板那非常有市场:这个功能很不错、怎么实现我不管、“现在就要!”。 + + + +简单点说:中华田园敏捷开发的核心在于“快”,这种“快”是拍脑袋的快,基本由老板或业务负责人,又或者产品经理来决定上线时间,研发leader和码农无权干涉,甚至无权提意见。 + + + +如果提?轻则挨批,重则滚蛋! + + + +在他们眼里,码农们甚至技术总监、CTO都是昂贵的耗材:LZ给你们这么多钱,养着你们、供着你们,让你明天上线居然做不到?那还要你们干啥,给我G。 + + + +那么除了能祝这样的公司早日倒闭,这样的老板早日破产,技术负责人们还能做什么呢? + + + +简单给几点建议: + + + +**1.打不过就加入** + +技术负责人经常要面对老板、运营负责人、产品负责人等多维度的质疑和围攻毒打。 + + + +相信我,如果你不是天赋异禀那么只靠技术显然是打不过他们的。 + + + +但如果你即精通技术,恰好又懂一点点产品运营,那么事情就开始发生微妙的变化了。 + + + +这时候你是绝对的T型人才,能很轻松的找到技术-产品、技术-运营的效率最大化边界。同时因为你懂,所以也不会被忽悠被牵着鼻子走。 + + + +**2.学会其他岗位的“沟通语言”** + + + +技术负责人需要和不同部门协调资源、推进合作、判断需求,有时候可能还要做跨公司的沟通。 + + + +有时候,技术人和其他部门的同事会有沟通困难的感觉,甚至相互都觉得对方不能理解自己,其实往往是沟通语言上出了问题。 + + + +技术人很容易陷入只说技术指标、技术特征、技术选型的陷阱。往往把对方讲得一头雾水还很自嗨,但这一刻沟通就已经失败了。 + + + +破解之道在于,和产品沟通就多说点技术提升对产品效用的影响,和运营沟通就多说点对用户ltv、转化率的帮助等等。 + + + +**3.不断降低老板的预期** + + + +预期这玩意一旦被拉起来就降不下去,对于技术负责人来说降低老板预期非常重要。 + + + +尤其是面对不懂技术的老板,他只知道:你前几次都搞定了,这次肯定没问题。 + + + +这个时候就要提前预警,告知老板事情的风险点和困难度,不然出了成绩还可能被认为在偷奸耍滑。 + + + +有的朋友在面试的过程中就给老板拉满了预期:这些问题我入职后,统统解决。 + + + +入职后一开始还好,一些问题被解决了,哪一次没解决慢慢积累,老板的信任很快就不在了。 + + + +时刻谨记不要把话说满,宁愿超出预期给出惊喜,也不要承诺后给出惊吓。 + +**写在最后的话:** + + + +以上三点真做到了,能显著增加技术负责人的生存周期。但很遗憾因为技术的底层属性:不直接创造业绩和收入,技术人在不成熟的管理体系中就是被当作耗材对待的,这一点无法撼动。 + + + +所以职场有个很奇怪的现象:各种CXO中CTO跳槽率是最频繁的,有时候真的是被逼的。 + + + +最后,祝每一个技术人都能被善待吧! + + + +今天就写这么多了,我们下期见~ + + + +**其他原创:** + +[美国计算机高校YYDS?](http://mp.weixin.qq.com/s?__biz=MzA3MzA5MTU4NA==&mid=2247503124&idx=1&sn=b76b916b5c152c93251a4d0a2381933e&chksm=9f16d27ba8615b6df913c9334a44877707fa78fad5307195915eb3d16427ce5cc3d5436fb067&scene=21#wechat_redirect) + + + +[涨薪拖了两年了....](http://mp.weixin.qq.com/s?__biz=MzA3MzA5MTU4NA==&mid=2247503020&idx=1&sn=a1fa09c4e98828497effe7575434ea5a&chksm=9f16d3c3a8615ad5512f23b1a6b4cbeb9738bd34b2b94960094c3c61967272985d04aa6a3986&scene=21#wechat_redirect) + + + +[晋升阿里P7,真的难!](http://mp.weixin.qq.com/s?__biz=MzA3MzA5MTU4NA==&mid=2247502948&idx=1&sn=cfcf909f631c8c1958e9fce8ec6c6963&chksm=9f16d30ba8615a1dec79c56eac90d825411513d18d9ca6353bae6585c81c03f9bb3111c68e1d&scene=21#wechat_redirect) + + + +[上海交大本硕转码求职春招,0 offer!](http://mp.weixin.qq.com/s?__biz=MzA3MzA5MTU4NA==&mid=2247502418&idx=1&sn=438a85f6dc66bafd7ca2ad5754852473&chksm=9f16d53da8615c2bdb945192152eec56e2f8e2ac756b2db444862a0a9fcd9a34546975796061&scene=21#wechat_redirect) + + + +PS:最近弄了一个新微信号,欢迎大家围观,洋哥是个不错的段子手、鸡汤大师: + +###### ![](https://mmbiz.qpic.cn/mmbiz_png/ibDBKEpdfwEB360Zz7JnldNY0KWTziciaR8ztEcJzZqR51zkSQvqHjASCiamVsJlVydx8RV2DZYdndlr2Djiarn383Q/640?wx_fmt=png) + + + +![](https://mmbiz.qpic.cn/mmbiz_jpg/ibDBKEpdfwEAvyxa3myBjNQeKlsX1uB3mmcpMQbSEBcAYJxaIAaH3maS9cMA8YMqgzDnF1v8iarDFnAuJNquR4Cg/640?wx_fmt=jpeg) + + + + + + + + + +你好,我是findyi,毕业于华科、清华,在腾讯做过码农,在360做过技术总监,在独角兽公司做过技术VP,[**点击蓝字查看我的成长之路**。](http://mp.weixin.qq.com/s?__biz=MzA3MzA5MTU4NA==&mid=2247489765&idx=2&sn=b788f6fa006292813ffd57186732e7cb&chksm=9f15078aa8628e9ce27f2ffd32ebb930a5b6c568a366c00b737012abf4fb2039740272741a8c&scene=21#wechat_redirect) + + + +创业五年虽未获成功,但心中的小火苗一直还在燃烧,看尽南山&西北旺的风光,但依然有着内心执着的追求。 + + + +目前我是一位大厂的业务负责人,同时,也是一位信奉终身成长的职场人。 + + + +同时,我也是知乎博主:findyi,日常分享高质量职场认知、技术干货欢迎围观。 \ No newline at end of file diff --git a/images/.DS_Store b/images/.DS_Store index 1a27fb9b7e2fe2fa4ac6eccf87738a23fbbbc82a..802788b3cd79b63c58929978a3b4f12fd9ce1aac 100644 Binary files a/images/.DS_Store and b/images/.DS_Store differ diff --git a/images/nice-article/jiagoushizenyanghuizhijitongjiagoulantu.jpg b/images/nice-article/jiagoushizenyanghuizhijitongjiagoulantu.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ae5a0631cd233cd9e7cebd1127d917e4944726ce Binary files /dev/null and b/images/nice-article/jiagoushizenyanghuizhijitongjiagoulantu.jpg differ diff --git a/images/nice-article/jishuzongjianbeikaichuliao.jpg b/images/nice-article/jishuzongjianbeikaichuliao.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7876a36f77724a0a4f892b7d7ad2121a12f59348 Binary files /dev/null and b/images/nice-article/jishuzongjianbeikaichuliao.jpg differ