Git and GitHub Secrets

记住密码 Git 记住密码配置后,不用每次 pull、push 都需要输入密码: git config --global credential.helper store 会在 cat ~/.gitconfig 看到: [credential] helper = store 快速检出上一个分支 git checkout - 提交空改动 git commit -m "empty commit" --allow-empty 在如下几种情况下是有意义: 标记一批工作或一个新功能的开始。 记录你对项目进行了跟代码无关的改动。 跟使用你仓库的其他人交流。 作为仓库的第一次提交,因为第一次提交日后是不能被 rebase 的:git commit -m "init repo" --allow-empty。 更直观的 status git status -sb 更直观的 log git log --all --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an %ae>%Creset' --abbrev-commit --date=relative 提交信息查询 找到其中和搜索条件相匹配的最近的一条。query (区别大小写)是你想要搜索的词语。 git show :/query 分支合并 显示所有已经合并到你当前分支的分支列表: git branch --merged 相反地: ...

July 12, 2021 · 1 min · 174 words · Me

如何做好技术协同及管理

《如何做好技术协同及管理 —— 合作伙伴篇》一次沙龙后的笔记。 招人 注重招人环节;在这一步要卡严,因为入职后再折腾就更费时费力了。 要关注的点:基础水平(工程能力)、离职原因(是否能长久)、个人性格(气场是否相合)。 入职 主动沟通是重要的特质。还有责任心、可塑性、执行力。 前紧后松;前期要做 code review,养成好习惯。 关注测试同学以及其他同学对其的反馈。了解工作状态、质量。 在突破底线后应该当机立断,无需心存幻想。 心态 没有归宿感;owner 意识。 每一位都是组内的一员。 管理 早会;任务安排,跟踪进度。不能不闻不问,最后容易抓瞎。 超过 30min 解决不了的问题,直接沟通,避免团队时间的浪费。 读书会,组内分享;打造一个好的技术氛围,这里不只是工作,还能成长。 要对任务的工作量有判断;并且明确开发规模,并且进行核对,防止 “大事小做,小题大做”。 通过先编写好单元测试、框架结构、interface,控制需求实现、代码质量。 在无法进行横向对比的情况下,可以对比加入前后是否释放自身生产力来进行判断。 三个卡点:需求评审、技术实现评审、测试用例评审,把关质量。 – EOF –

July 8, 2021 · 1 min · 31 words · Me

设计模式之美 Part2

11 领域驱动设计(Domain Driven Design,简称 DDD)。 什么是基于贫血模型的传统开发模式? UserEntity 和 UserRepository 组成了数据访问层,UserBo 和 UserService 组成了业务逻辑层,UserVo 和 UserController 在这里属于接口层。 Service 层的数据和业务逻辑,被分割为 BO 和 Service 两个类中。像 UserBo 这样,只包含数据,不包含业务逻辑的类,就叫作贫血模型(Anemic Domain Model)。 这种贫血模型将数据与操作分离,破坏了面向对象的封装特性,是一种典型的面向过程的编程风格。 什么是基于充血模型的 DDD 开发模式? 领域驱动设计,即 DDD,主要是用来指导如何解耦业务系统,划分业务模块,定义业务领域模型及其交互。 基于贫血模型的传统的开发模式,重 Service 轻 BO;基于充血模型的 DDD 开发模式,轻 Service 重 Domain。 为什么基于贫血模型的传统开发模式如此受欢迎? 系统业务可能都比较简单,简单到就是基于 SQL 的 CRUD 操作 充血模型的设计要比贫血模型更加有难度。我们从一开始就要设计好针对数据要暴露哪些操作,定义哪些业务逻辑。而不是像贫血模型那样,我们只需要定义数据,之后有什么功能开发需求,我们就在 Service 层定义什么操作,不需要事先做太多设计。 思维已固化,转型有成本。 什么项目应该考虑使用基于充血模型的 DDD 开发模式? 适合业务复杂的系统开发。比如,包含各种利息计算模型、还款模型等复杂业务的金融系统。 两种不同的开发模式会导致不同的开发流程。基于充血模型的 DDD 开发模式的开发流程,在应对复杂业务系统的开发的时候更加有优势。 DDD 这种开发模式下,我们需要事先理清楚所有的业务,定义领域模型所包含的属性和方法。领域模型相当于可复用的业务中间层。新功能需求的开发,都基于之前定义好的这些领域模型来完成。 12 一个虚拟钱包系统 充值、提现、支付、查询余额、查询交易流水。甚至还有冻结、透支、转赠等。 整个钱包系统一部分单纯跟应用内的虚拟钱包账户打交道,另一部分单纯跟银行账户打交道。我们基于这样一个业务划分,给系统解耦,将整个钱包系统拆分为两个子系统:虚拟钱包系统和三方支付系统。 不保证数据的强一致性,只实现数据的最终一致性。 public class VirtualWalletService { // 通过构造函数或者 IOC 框架注入 private VirtualWalletRepository walletRepo; private VirtualWalletTransactionRepository transactionRepo; public VirtualWallet getVirtualWallet(Long walletId ) { VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId); VirtualWallet wallet = convert(walletEntity); return wallet; } public BigDecimal getBalance(Long walletId) { return virtualWalletRepo.getBalance(walletId); } public void debit(Long walletId, BigDecimal amount) { VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId); // 贫血型 // BigDecimal balance = walletEntity.getBalance(); // if (balance.compareTo(amount) < 0) { // throw new NoSufficientBalanceException(...); // } // walletRepo.updateBalance(walletId, balance.subtract(amount)); // DDD VirtualWallet wallet = convert(walletEntity); wallet.debit(amount); walletRepo.updateBalance(walletId, wallet.balance()); } public void credit(Long walletId, BigDecimal amount) { VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId); // 贫血型 // BigDecimal balance = walletEntity.getBalance(); // walletRepo.updateBalance(walletId, balance.add(amount)); // DDD VirtualWallet wallet = convert(walletEntity); wallet.credit(amount); walletRepo.updateBalance(walletId, wallet.balance()); } public void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) { VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactionEntity(); transactionEntity.setAmount(amount); transactionEntity.setCreateTime(System.currentTimeMillis()); transactionEntity.setFromWalletId(fromWalletId); transactionEntity.setToWalletId(toWalletId); transactionEntity.setStatus(Status.TO_BE_EXECUTED); Long transactionId = transactionRepo.saveTransaction(transactionEntity); try { debit(fromWalletId, amount); credit(toWalletId, amount); } catch (InsufficientBalanceException e) { transactionRepo.updateStatus(transactionId, Status.CLOSED); // ...rethrow exception e... } catch (Exception e) { transactionRepo.updateStatus(transactionId, Status.FAILED); // ...rethrow exception e... } transactionRepo.updateStatus(transactionId, Status.EXECUTED); } } 领域模型 VirtualWallet 类很单薄,包含的业务逻辑很简单。相对于原来的贫血模型的设计思路,这种充血模型的设计思路,貌似并没有太大优势。这也是大部分业务系统都使用基于贫血模型开发的原因。不过,如果虚拟钱包系统需要支持更复杂的业务逻辑,那充血模型的优势就显现出来了。比如,我们要支持透支一定额度和冻结部分余额的功能。这个时候,我们重新来看一下 VirtualWallet 类的实现代码。 ...

February 6, 2021 · 3 min · 505 words · Me

设计模式之美 Part1

00 KISS 原则(Keep It Simple and Stupid),这个原则理解起来很简单,一看貌似就懂了,那我问你,怎样的代码才算是足够简单呢?怎样才算不够简单需要优化呢? “Talk is cheap, show me the code.” 01 为什么要学习设计模式:应对面试中的设计模式相关问题;告别写被人吐槽的烂代码;提高复杂代码的设计和开发能力;让读源码、学框架事半功倍;为你的职场发展做铺垫。 02 灵活性(flexibility)、可扩展性(extensibility)、可维护性(maintainability)、可读性(readability)、可理解性(understandability)、易修改性(changeability)、可复用(reusability)、可测试性(testability)、模块化(modularity)、高内聚低耦合(high cohesion loose coupling)、高效(high effciency)、高性能(highperformance)、安全性(security)、兼容性(compatibility)、易用性(usability)、整洁(clean)、清晰(clarity)、简单(simple)、直接(straightforward)、少即是多(less code is more)、文档详尽(well-documented)、分层清晰(well-layered)、正确性(correctness、bug free)、健壮性(robustness)、鲁棒性(robustness)、可用性(reliability)、可伸缩性(scalability)、稳定性(stability)、优雅(elegant)、好(good)、坏(bad) 我们并不能通过单一的维度去评价一段代码写的好坏。比如,即使一段代码的可扩展性很好,但可读性很差,那我们也不能说这段代码质量高。 如果用数字来量化代码的可读性的话,它应该是一个连续的区间值,而非 0、1 这样的离散值。 代码质量的评价有很强的主观性。 有些词语过于笼统、抽象,比较偏向对于整体的描述,比如优雅、好、坏、整洁、清晰等;有些过于细节、偏重方法论,比如模块化、高内聚低耦合、文档详尽、分层清晰等;有些可能并不仅仅局限于编码,跟架构设计等也有关系,比如可伸缩性、可用性、稳定性等。 可维护性(maintainability) 破坏原有代码设计、不引入新的 bug 的情况下,能够快速地修改或者添加代码。与之相反,修改或者添加代码需要冒着极大的引入新 bug 的风险,并且需要花费很长的时间才能完成。 码分层清晰、模块化好、高内聚低耦合、遵从基于接口而非实现编程的设计原则等等,那就可能意味着代码易维护。 可读性(readability) “任何傻瓜都会编写计算机能理解的代码。好的程序员能够编写人能够理解的代码。” 是否符合编码规范、命名是否达意、注释是否详尽、函数是否长短合适、模块划分是否清晰、是否符合高内聚低耦合等等。 code review 是一个很好的测验代码可读性的手段。如果你的同事可以轻松地读懂你写的代码,那说明你的代码可读性很好;如果同事在读你的代码时,有很多疑问,那就说明你的代码可读性有待提高了。 可扩展性(extensibility) 我们在不修改或少量修改原有代码的情况下,通过扩展的方式添加新的功能代码。说直白点就是,代码预留了一些功能扩展点,你可以把新功能代码,直接插到扩展点上,而不需要因为要添加一个功能而大动干戈,改动大量的原始代码。 “对修改关闭,对扩展开放”。 灵活性(flexibility) 如果一段代码易扩展、易复用或者易用,我们都可以称这段代码写得比较灵活。 当我们添加一个新的功能代码的时候,原有的代码已经预留好了扩展点,我们不需要修改原有的代码,只要在扩展点上添加新的代码即可。这个时候,我们除了可以说代码易扩展,还可以说代码写得好灵活。 当我们要实现一个功能的时候,发现原有代码中,已经抽象出了很多底层可以复用的模块、类等代码,我们可以拿来直接使用。这个时候,我们除了可以说代码易复用之外,还可以说代码写得好灵活。 当我们使用某组接口的时候,如果这组接口可以应对各种使用场景,满足各种不同的需求,我们除了可以说接口易用之外,还可以说这个接口设计得好灵活或者代码写得好灵活。 简洁性(simplicity) 尽量保持代码简单。代码简单、逻辑清晰,也就意味着易读、易维护。我们在编写代码的时候,往往也会把简单、清晰放到首位。 KISS 原则,思从深而行从简,真正的高手能云淡风轻地用最简单的方法解决最复杂的问题。这也是一个编程老手跟编程新手的本质区别之一。 可复用性(reusability) 尽量减少重复代码的编写,复用已有的代码。 当讲到面向对象特性的时候,我们会讲到继承、多态存在的目的之一,就是为了提高代码的可复用性;当讲到设计原则的时候,我们会讲到单一职责原则也跟代码的可复用性相关;当讲到重构技巧的时候,我们会讲到解耦、高内聚、模块化等都能提高代码的可复用性。可见,可复用性也是一个非常重要的代码评价标准,是很多设计原则、思想、模式等所要达到的最终效果。 DRY(Don’t Repeat Yourself)设计原则。 可测试性(testability) 代码可测试性的好坏,能从侧面上非常准确地反应代码质量的好坏。代码的可测试性差,比较难写单元测试,那基本上就能说明代码设计得有问题。 03 面向对象 面向对象的四大特性:封装、抽象、继承、多态面 向对象编程与面向过程编程的区别和联系 面向对象分析、面向对象设计、面向对象编程 接口和抽象类的区别以及各自的应用场景 基于接口而非实现编程的设计思想 多用组合少用继承的设计思想 面向过程的贫血模型和面向对象的充血模型 设计原则 指导我们代码设计的一些经验总结。 ...

February 5, 2021 · 2 min · 333 words · Me

MySQL Code Snippet

Tips SQL -- 追踪优化器 Trace 功能 -- optimizer_trace_enabled=1 -- optimizer_trace_file=optimizer_trace.log SELECT @@optimizer_trace; SET optimizer_trace = 'enabled=on'; -- <your query>; SET optimizer_trace = 'enabled=off'; select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE; -- 查看优化后的 SQL -- 在联表查询时比较有效果 EXPLAIN <你的 SQL>; SHOW WARNINGS; -- 查看处理 SHOW PROCESSLIST; -- 查看结构 DESC user; SHOW COLUMNS FROM user; DESCRIBE user; 视图更新 CREATE OR REPLACE VIEW 视图名 AS SELECT[...] FROM [...] 字符集转换 LEFT JOIN code_value b ON a.cost_type = CONVERT ( b.`code` USING utf8mb4 ) COLLATE utf8mb4_unicode_ci Function -- https://www.w3schools.com/sql/func_mysql_find_in_set.asp select FIND_IN_SET('bb', 'aa,bb,cc'); select FIND_IN_SET(null, '0'); -- COALESCE 函数用于返回参数列表中第一个非 NULL 值。如果所有参数都为 NULL,则返回 NULL COALESCE ( `code_value`.`name`, '' ) AS cost_type_name, ON vs USING MySQL ON vs USING? | stackoverflow ...

January 29, 2021 · 2 min · 247 words · Me

PHP 月份加减问题

看现象 var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2020-07-31")))); // string(10) "2020-08-31" 符合预期 var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2020-05-31")))); // string(10) "2020-07-01" 不符合预期,预期 2020-06-30 var_dump(date("Y-m-d", strtotime("-1 month", strtotime("2020-02-29")))); // string(10) "2020-01-29" 符合预期 var_dump(date("Y-m-d", strtotime("-1 month", strtotime("2020-03-31")))); // string(10) "2020-03-02" 不符合预期,预期 2020-02-29 // Carbon\Carbon Carbon::parse("2020-07-31")->addMonth()->toDateString(); // "2020-08-31" Carbon::parse("2020-05-31")->addMonth()->toDateString(); // "2020-07-01" Carbon::parse("2020-02-29")->subMonth()->toDateString(); // "2020-01-29" Carbon::parse("2020-03-31")->subMonth()->toDateString(); // "2020-03-02" // 结果与 strtotime 一致。 原因 var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2020-05-31")))); // string(10) "2020-07-01" date 内部的处理逻辑: 2020-05-31 做 +1 month 也就是 2020-06-31。 再做日期规范化,因为没有 06-31,所以 06-31 就等于了 07-01。 var_dump(date("Y-m-d", strtotime("2020-06-31"))); // string(10) "2017-07-01" var_dump(date("Y-m-d", strtotime("next month", strtotime("2017-01-31")))); // string(10) "2017-03-03" var_dump(date("Y-m-d", strtotime("last month", strtotime("2017-03-31")))); // string(10) "2017-03-03" 解决方案 var_dump(date("Y-m-d", strtotime("last day of -1 month", strtotime("2017-03-31")))); // string(10) "2017-02-28" var_dump(date("Y-m-d", strtotime("first day of +1 month", strtotime("2017-08-31")))); // string(10) "2017-09-01" // 但要注意短语的含义: var_dump(date("Y-m-d", strtotime("last day of -1 month", strtotime("2017-03-01")))); // string(10) "2017-02-28" 如果使用 Carbon\Carbon 可以用 subMonthNoOverflow 与 addMonthNoOverflow 防止进位: ...

January 27, 2021 · 2 min · 271 words · Me

回顾 2020

过年就在石家庄 lwl 走学 肺炎在家办公 PR 剪辑 表情包制作 自己染发剪发 – EOF –

December 31, 2020 · 1 min · 11 words · Me

MySQL 空格问题

看现象 创建一个测试数据库表,插入测试数据: CREATE TABLE `blank_space` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `uid` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `desc` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `uniq_key` (`uid`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; INSERT INTO `blank_space`(`id`, `uid`, `desc`) VALUES (1, 'abc ', '末尾1个'); INSERT INTO `blank_space`(`id`, `uid`, `desc`) VALUES (2, ' abc', '开头1个'); INSERT INTO `blank_space`(`id`, `uid`, `desc`) VALUES (3, ' abc', '开头2个'); id uid desc 1 abc_ 末尾 1 个 2 _abc 开头 1 个 3 __abc 开头 2 个 uid 实际上没有 _,这样写是为了看到空格。 ...

November 28, 2020 · 2 min · 367 words · Me

Composer 文档笔记

Basic usage https://getcomposer.org/doc/01-basic-usage.md # get a list of your locally available platform packages. # php | ext-<name> | lib-<name> composer show --platform Libraries https://getcomposer.org/doc/02-libraries.md Light-weight distribution packages 轻量级分发包。使用 .gitattributes 来防止不需要的文件使 zip 分发包膨胀。 // .gitattributes /demo export-ignore phpunit.xml.dist export-ignore /.github/ export-ignore 通过检查手动生成的压缩文件进行测试: git archive branchName --format zip -o file.zip Command-line interface / Commands https://getcomposer.org/doc/03-cli.md As Composer uses symfony/console you can call commands by short name if it’s not ambiguous. ...

October 28, 2020 · 4 min · 802 words · Me, LLM

在 Laravel 之外使用 illuminate 组件

当代框架基本都是有组件构成,这使得框架变得更加灵活。The Laravel Components | github Laravel 中有不少优质组件,那如何在 Laravel 之外使用 illuminate 组件呢? illuminate/validation 以 illuminate/validation 为例,validation 有丰富的数据验证功能。 在项目的 composer.json 文件中添加: ... "require": { ... "illuminate/validation": "^5.8", ... 从 Laravel-Lang/lang 项目中复制需要的语言文件放到自己的项目中。 例如:在 Yii2 项目中,复制对应语言文件到项目中的 assets/lang/zh-CN/validation.php。 创建 common/Validator.php: <?php namespace app\common; use Illuminate\Filesystem\Filesystem; use Illuminate\Translation\FileLoader; use Illuminate\Translation\Translator; use Illuminate\Validation\Factory; class Validator { private static $instance = null; private function __construct() { } public static function getInstance(): Factory { if (null === static::$instance) { $translationPath = get_alias('@assets/lang'); $translationLocale = 'zh-CN'; $transFileLoader = new FileLoader(new Filesystem(), $translationPath); $translator = new Translator($transFileLoader, $translationLocale); static::$instance = new Factory($translator); } return static::$instance; } } 在全局函数文件添加: // https://learnku.com/docs/laravel/5.8/validation/3899#manually-creating-validators // $rules = [ // 'name' => 'required|string|min:2|max:5', // 'code' => 'required|string|min:2|max:5', // ]; function validator(array $data, array $rules, array $messages = [], array $customAttributes = []) { return \app\common\Validator::getInstance()->make($data, $rules, $messages, $customAttributes); } 测试使用: ...

September 11, 2020 · 1 min · 173 words · Me