少湖说 | 科技自媒体

互联网,科技,数码,鸿蒙

个人介绍

少湖,独立开发者,全栈技术专家,HarmonyOS 开发者达人,华为开发者专家(HDE),坚果派专家,Openharmony 布道师。

社交媒体

公众号

少湖说:少胡说,多观察。

关注公众号,加入交流群。

编者按:如何写出更优雅的代码?这是一个恒久的问题,

在 Java 的世界里,SpringBoot 框架最为流程,几乎已经成为事实标准。本系列将围绕 SpringBoot 实战案例进行,介绍如何写出更优雅的 Java 代码。
笔者试图通过一个个的实际案例,抽丝剥茧,探讨 Spring 的设计哲学,探讨 Java 美学。

数据更新案例

数据更新的业务逻辑,很常见,举例例子,这里有一个更新发票信息的业务逻辑,按常规的思路,很容易写出这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function updateInvoice(UpdateInvoiceForm form) {
Invoice updateInvoice = new Invoice();
updateInvoice.setId(form.getId());
// 更新发票号
if(StringUtils.isNotBlank(form.getInvoiceTaxNumber())) {
updateInvoice.setInvoiceTaxNumber(form.getInvoiceTaxNumber());
}
// 税额
if(form.getTaxAmount() != null) {
updateInvoice.setTaxAmount(form.getTaxAmount());
}
// 税前金额
if(form.getTotalAmountPreTax() != null) {
updateInvoice.setTotalAmountPreTax(form.getTotalAmountPreTax());
}

invoiceMapper.updateByPrimaryKeySelective(updateInvoice);
}

上面的逻辑也简单清晰,只有当某个上传的字段不为空时,才会更新数据库中相应的字段。那么,如果字段很多时,这样的代码就看起来非常冗余,那么,如何优化呢?

我们知道,Java 8 的 Optional 类,有着这样的用户:

1
optionalVal.ifPresent(value -> xxx);

当前我们可以直接使用 Optional 类,将字段的更新逻辑封装在 Optional 类中,这样,代码就显得非常简洁,就像这样:

1
Optional.ofNullable(form.getTaxAmount()).ifPresent(taxAmount -> {updateInvoice.setTaxAmount(taxAmount);})

不过这也有个问题,ifPresent 只支持判断对象是否为 null,无法过滤字符串为空的情况,基于这样的需要,我们可以仿照 Optional 类,自己实现一个封装

其核心代码像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
public OptionalUtil<T> ifNotNull(Consumer<T> action) {
if (value != null) {
action.accept(value);
}
return this;
}

public OptionalUtil<T> ifNotBlank(Consumer<String> action) {
if (value instanceof String && StringUtils.isNotBlank((String) value)) {
action.accept((String) value);
}
return this;
}

上面定义了两个核心方法,ifNotNull 和 ifNotBlank,分别用于判断对象是否为 null 和字符串是否为空,当满足条件时,再执行闭包里面的操作。

以下是完整的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import java.util.function.Consumer;

import org.apache.commons.lang3.StringUtils;

public class OptionalUtil<T> {

private final T value;

private OptionalUtil(T value) {
this.value = value;
}

public static <T> OptionalUtil<T> valueOf(T value) {
return new OptionalUtil<>(value);
}

public OptionalUtil<T> ifBlank(Consumer<String> action) {
if (value instanceof String && StringUtils.isBlank((String) value)) {
action.accept((String) value);
}
return this;
}

public OptionalUtil<T> ifNotBlank(Consumer<String> action) {
if (value instanceof String && StringUtils.isNotBlank((String) value)) {
action.accept((String) value);
}
return this;
}

public OptionalUtil<T> ifNull(Consumer<T> action) {
if (value == null) {
action.accept(value);
}
return this;
}

public OptionalUtil<T> ifNotNull(Consumer<T> action) {
if (value != null) {
action.accept(value);
}
return this;
}

public OptionalUtil<T> ifZero(Consumer<T> action) {
if (isZeroValue(value)) {
action.accept(value);
}
return this;
}

private boolean isZeroValue(Object value) {
if (value instanceof Number && ((Number) value).doubleValue() == 0.0d) {
return true;
}
if (value instanceof java.util.Date && ((java.util.Date) value).getTime() <= 0) {
return true;
}
return false;
}

public OptionalUtil<T> ifNotZero(Consumer<T> action) {
if (!isZeroValue(value)) {
action.accept(value);
}
return this;
}

public void execute() {
// 可以用于触发链式调用的结束,也可以扩展为执行某些默认操作
}
}

可以看到,这里面封装了一个 OptionalUtil 类,用于处理 Optional 类的链式调用。里面包括一个静态构造方法 valueOf, 同时支持各类不同的类型,
不论是 String,还是继承 Number 的类,如 Integer,Long,BigDecimal 等,进行判断。

时间 ifZero 也支持时间类型,如果传的时间为 1970-01-01 00:00:00 及以前的时间,也视做零值。

这样,我们的更新代码就可以写成这样:

1
2
3
4
5
6
7
8
9
10
11
12
public function updateInvoice(UpdateInvoiceForm form) {
Invoice updateInvoice = new Invoice();
updateInvoice.setId(form.getId());
// 更新发票号
OptionalUtil.valueOf(form.getInvoiceTaxNumber()).ifNotBlank(updateInvoice::setInvoiceTaxNumber);
// 税额
OptionalUtil.valueOf(form.getTaxPreAmount()).ifNotBlank(updateInvoice::setTaxPreAmount);
// 税前金额
OptionalUtil.valueOf(form.getTotalAmountPreTax()).ifNotBlank(updateInvoice::setTotalAmountPreTax);

invoiceMapper.updateByPrimaryKeySelective(updateInvoice);
}

这样,优雅的数据更新就实现了,你觉得怎么样呢,欢迎在评论区交流。

1.业务域名/网页授权域名绑定失败

在服务号中添加业务域名/网页授权域名时,需要先下载验证文件,将文件上传至服务器

alt text

文件上传到网站根目录后,仍然验证失败

解决方法

不少网站系统的公开目录并不是网站根目录,比如大部分 PHP 系统有可能是 public 目录,这里,需要将文件上传至 public 目录下

2.网站首页 404

网站代码上传完成后,访问网站首页,会提示 404 NOT FOUND 错误

解决方法

确定网站使用的框架,比如 Laravel,ThinkPHP 等,默认的运行目录需要指定到 public 目录下,因为其入口文件 index.php 位于 public 目录下

所以需要修改网站配置文件,将网站运行目录修改为 public

alt text

免费领取云主机

点击免费领取链接, 登录华为云账号,免费领取云主机。

如果没有华为账号的话,先点击注册;有账号的话直接登录。

alt text

根据提示,填写手机号和密码,完成注册。

alt text

然后找到配置云主机

alt text

根据下面的提示选择配置,然后点击安装。

alt text

这样云主机就领取并安装好了。

启动云主机

接下来,我们进入云主机安装宝塔面板,快速建站。

首先点击打开云主机,选择进入桌面

alt text

打开云主机,会进入初始化界面,等待几分钟

alt text

然后就看到云主机的桌面了

alt text

安装宝塔面板

在桌面的左下角,找到命令行工具,也就是 Terminal Emulator,长这个样子

alt text

我们打开命令行工具,然后从宝塔官方下载页面,复制安装命令,复制时我们选择 Ubuntu/Debain 的安装命令:

我们在刚才打开的命令行工具中,右键,选择粘贴,将刚才复制的命令粘贴到命令行工具中,然后回车

alt text

在刚开始运行时,程序会提示我们确认(Do you want to install Bt-Panel to the /www directory now?(y/n)),我们输入 y,表示确认,然后加车,开始安装。

alt text

等到一段时间的等待之后,命令行中会提示安装成功,像下面这样

alt text

注意,你需要把上面“面板账户登录信息”全部复制并记录下来,以登录时使用。

选中以后,右键复制,将信息保存到本地记录本中。

登录并配置宝塔面板

在桌面找到火狐浏览器(FireFox)打开,输入刚才记录的内网面板地址

需要注意的是,这款云主机,并不支持外网IP访问

alt text

然后输入前面记录的账号密码,登录宝塔面板

alt text

首次使用会弹出用户协议,滚动鼠标滚轮至最后,完成阅读,勾选同意,进入面板

alt text

如果你之前没有使用过宝塔面板,则需要绑定宝塔官方账号,这里我还没有注册,所有先选择注册,点击后打开注册页面

alt text

按提示输入注册信息,并完成注册

alt text

注册成功后,就可以关闭当前页面了,回到之前的宝塔面板,登录注册账号。绑定成功后,将弹出初始化推荐配置对话框:

alt text

因为我们要通过 Docker 安装 DeekSeep,这里我们暂时跳过配置。

打开左侧的菜单,找到 Docker,点击进入,然后点击立即安装。

alt text

选择默认安装方式,点击确认

alt text

等待安装成功后,我们搜索 DeepSeek,点击安装

alt text

等待安装成功

alt text

经过漫长的等待,安装成功

alt text

接下来,我们在火狐浏览器中打开Open WebUI,输入 http://localhost:18480

alt text

点击开始使用

接下来输入管理员账号和密码

alt text

最好,我们可以愉快的使用 DeepSeek 了!任何关于网站系统运维的问题,都可以帮你解答。

alt text

参考资料

免费领取云主机

点击免费领取链接, 登录华为云账号,免费领取云主机。

如果没有华为账号的话,先点击注册;有账号的话直接登录。

alt text

根据提示,填写手机号和密码,完成注册。

alt text

然后找到配置云主机

alt text

根据下面的提示选择配置,然后点击安装。

alt text

这样云主机就领取并安装好了。

启动云主机

接下来,我们进入云主机安装宝塔面板,快速建站。

首先点击打开云主机,选择进入桌面

alt text

打开云主机,会进入初始化界面,等待几分钟

alt text

然后就看到云主机的桌面了

alt text

安装宝塔面板

在桌面的左下角,找到命令行工具,也就是 Terminal Emulator,长这个样子

alt text

我们打开命令行工具,然后从宝塔官方下载页面,复制安装命令,复制时我们选择 Ubuntu/Debain 的安装命令:

我们在刚才打开的命令行工具中,右键,选择粘贴,将刚才复制的命令粘贴到命令行工具中,然后回车

alt text

在刚开始运行时,程序会提示我们确认(Do you want to install Bt-Panel to the /www directory now?(y/n)),我们输入 y,表示确认,然后加车,开始安装。

alt text

等到一段时间的等待之后,命令行中会提示安装成功,像下面这样

alt text

注意,你需要把上面“面板账户登录信息”全部复制并记录下来,以登录时使用。

选中以后,右键复制,将信息保存到本地记录本中。

登录并配置宝塔面板

在桌面找到火狐浏览器(FireFox)打开,输入刚才记录的内网面板地址

需要注意的是,这款云主机,并不支持外网IP访问

alt text

然后输入前面记录的账号密码,登录宝塔面板

alt text

首次使用会弹出用户协议,滚动鼠标滚轮至最后,完成阅读,勾选同意,进入面板

alt text

如果你之前没有使用过宝塔面板,则需要绑定宝塔官方账号,这里我还没有注册,所有先选择注册,点击后打开注册页面

alt text

按提示输入注册信息,并完成注册

alt text

注册成功后,就可以关闭当前页面了,回到之前的宝塔面板,登录注册账号。绑定成功后,将弹出初始化推荐配置对话框:

alt text

我们选择默认配置即可,点击最左侧LNMP(推荐)下面的一键安装按钮,等待安装完成。

alt text

创建站点

在菜单中打开网站,点击添加站点

alt text

输入绑定的域名或者 IP, 数据库选择 MySQL, 点击确定创建

alt text

创建成功,注意保存数据库用户名和密码。

alt text

访问站点

在火狐浏览器中,输入绑定的 IP,http://10.12.79.233,打开网站查看

alt text

参考资料

Flutter 3.27 支持的 Dart 版本为 3.6,本文说明如何将 TPC中的 animations 库升级适配Flutter 3.27

升级步骤:

  1. 找到上有 animations 库的最新版本及源码,并下载至本地解压

打开 https://github.com/flutter/packages,点击 Doanlowd Zip,解压到本地。

  1. 克隆 TPC 中的 flutter_packages 仓库

克隆之前我们先 Fork 一份仓库至自己的组织中,以便后续提交 PR 使用。克隆成功后,将 Fork 后的仓库克隆至本地

1
git clone https://gitcode.com/nutpi/flutter_packages.git
  1. 新建一个分支

按照命名规则,新建分支为:br_animations-v2.0.11_ohos

  1. 将解压的 animations 中的文件同步覆盖到 TPC 中的 flutter_packages/animations 文件夹下

需要注意的是,这里需要逐个文件覆盖,需要保留 TPC 仓库中的 ohos 相关文件目录

  1. 测试代码

5.1 打开 example 目录,首先对 ohos 项目进行签名配置

5.2 使用 Deveco 打开 example/ohos 目录,然后修改配置签名

5.3 运行 lib/main.dart 代码,测试功能是否正常

  1. 提交代码至自己的组织中

  2. 提交 PR

近日,OpenHarmony 社区的 Flutter 仓库创建了 Flutter 3.27.4-dev 分支,这标志着 鸿蒙版 Flutter SDK 3.27 进入开发阶段。

值得注意的是,Flutter 等三方框架从 SIG 组织中分离出来,使用单独的 TPC(Third Party Components)来管理,专门存放开源三方库,代码管理更加清晰,但社区管理上,TPC 仍然由 OpenHarmony SIG 主导。

准备工作

这里我们使用 FVM 管理Flutter SDK

首先进入 fvm/versions 目录下,克隆 3.27.4-dev 分支的 flutter_flutter 仓库

1
2
cd ~/fvm/versions
git clone -b oh-3.27.4-dev https://gitcode.com/openharmony-tpc/flutter_flutter.git custom_3.27.4

切换 Flutter SDK

接下来进入自己的项目,使用 fvm 切换版本

1
fvm use custom_3.27.4

参考资料

开通 WAF

首先找到 Web应用防火墙,然后点击开通。

添加域名

开通后打开接入管理,点击 CNAME 接入

本文章中的示例域名为 api.baidu.com,不代表实际域名,请自行替换。

alt text

接下来输入需要接入的域名

alt text

回到域名解析管理中,按照提示添加 TXT 记录

alt text

接下来,添加服务器地址,这里的地址就是防护的服务器/负载均衡的公网 IP地址

alt text

点击提交后,同样根据页面提示,添加 CNAME 记录

alt text

回到域名解析控制台,添加 CNAME 记录

alt text

检测 CNAME 是否生效

  1. 在 Window 上,可以使用 nslookup 进行测试
1
nslookup -qt=CNAME api.baidu.com

如果看到 api.baidu.com.c.yundunwaf5.com. 相关的输出,则说明 CNAME 配置成功。

  1. 在 Linux/Mac 上,可以使用 dig 进行测试
1
dig api.baidu.com CNAME
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
➜ dig api.baidu.com CNAME

; <<>> DiG 9.10.6 <<>> api.baidu.com CNAME
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50094
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;api.baidu.com. IN CNAME

;; ANSWER SECTION:
api.baidu.com. 10 IN CNAME api.baidu.com.c.yundunwaf5.com.

;; Query time: 41 msec
;; SERVER: 100.100.2.136#53(100.100.2.136)
;; WHEN: Wed Jun 25 19:34:27 CST 2025
;; MSG SIZE rcvd: 91

如上所示,看到 api.baidu.com.c.yundunwaf5.com. 输出,则说明 CNAME 配置成功。

腾讯 CloudBase 可以用于托管静态网站,服务开通之后,使用 CloudBase CLI 可以将本地静态网站上传到 CloudBase,并生成相应的访问域名。

配置 Workflow

创建 .github/workflows/deploy.yml 文件, 编辑内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
name: Deploy to CloudBase Static Hosting

on:
workflow_dispatch:
push:
branches:
- main

jobs:

deploy:
runs-on: ubuntu-latest

steps:
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18' # 根据您的项目需求选择Node.js版本

- name: Install CloudBase CLI
run: npm install -g @cloudbase/cli

- name: Deploy to CloudBase Static Hosting
run: |
tcb login --apiKeyId ${{ secrets.TCB_SECRET_ID }} --apiKey ${{ secrets.TCB_SECRET_KEY }}
tcb hosting deploy ./dist --envId ${{ secrets.TCB_ENV_ID }}

这里,我们首先配置好 node 环境,然后安装 CloudBase CLI,通过 tcb login 命令登录 CloudBase,然后使用 tcb hosting deploy 命令将静态网站部署到 CloudBase。

可以看到,这里用到了几个环境变量,如 TCB_SECRET_ID、TCB_SECRET_KEY、TCB_ENV_ID。 接下来,我们需要在项目设置中添加环境变量。

配置

  1. 点击 Settings 按钮,进入项目设置页面。找到 Secrets and Variables 选项展开,点击 Actions,在 Repository secrets 处点击

New repository secret 按钮,准备添加变量。

alt text

  1. 添加变量,分别添加 TCB_SECRET_ID、TCB_SECRET_KEY、TCB_ENV_ID。

alt text

TCB_SECRET_ID、TCB_SECRET_KEY,通过控制台/访问管理,找到访问密钥管理,添加。

TCB_ENV_ID 为服务创建好之后的环境 ID。

参考资料

准备工作

1.安装 DevEco Studio NEXT IDE, 注意版本应该是 Next,当前最新的是 Beta3

2.安装Git, 如果要同时适配安卓,需要安装Android Studio; 如果要适配ios,需要安装Xcode

Mac 安装(推荐)

环境变量配置

1
2
3
4
5
6
7
8
9
10
# Flutter Mirror
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

# HarmonyOS SDK
export TOOL_HOME=/Applications/DevEco-Studio.app/Contents/
export DEVECO_SDK_HOME=$TOOL_HOME/sdk # command-line-tools/sdk
export PATH=$TOOL_HOME/tools/ohpm/bin:$PATH # command-line-tools/ohpm/bin
export PATH=$TOOL_HOME/tools/hvigor/bin:$PATH # command-line-tools/hvigor/bin
export PATH=$TOOL_HOME/tools/node/bin:$PATH # command-line-tools/tool/node/bin

Windows 安装

配置用户变量

1
2
3
4
5
6
7
FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

PUB_HOSTED_URL=https://pub.flutter-io.cn

DEVECO_SDK_HOME=C:\Program Files\Huawei\DevEco Studio\sdk

JAVA_HOME=C:\Program Files\Huawei\DevEco Studio\jbr

配置环境变量

编辑 PATH,添加以下路径

1
2
3
4
5
6
7
C:\Program Files\Huawei\DevEco Studio\tools\ohpm\bin

C:\Program Files\Huawei\DevEco Studio\tools\hvigor\bin

C:\Program Files\Huawei\DevEco Studio\tools\node

C:\Program Files\Huawei\DevEco Studio\jbr\bin

管理多个 Flutter 版本

如果在项目开发中,需要使用多个 Flutter 版本,可以考虑使用 fvm

  1. 安装 FVM
  2. 使用 fvm 官方 flutter 版本
1
fvm install 3.22.0
  1. 安装自定义鸿蒙版本,进入 fvm/version 目录,通常位于用户目录下,如 ~/fvm/versions/3.22.0,
    拷贝仓库并重命名为 custom_x.y.z的名字
1
git clone -b dev https://gitcode.com/openharmony-sig/flutter_flutter.git custom_3.7.12

注意命名格式必须为 custom_x.y.z,即必须以 custom_ 开头,后面跟三位数字版本号,如 custom_3.7.12

  1. 在项目中使用单独的 flutter sdk 版本, 在项目目录中执行:
1
fvm use custom_3.7.12

常见问题

  1. 运行 flutter doctor 出现 Error: Unable to find git in your PATH.

执行以下命令

1
git config --global --add safe.directory '*'

案例

Flutter 鸿蒙交流群

【flutter鸿蒙技术交流群】

目前 Flutter 鸿蒙已经跑通,但仍然存在一些潜在的坑需要解决和处理,欢迎大家一起分享交流

请扫码加小助手进群:
备注:鸿蒙Flutter
wx:zacksleo

alt text

参考资料

0%