显示标签为“Python & Java & Rust”的博文。显示所有博文
显示标签为“Python & Java & Rust”的博文。显示所有博文

2024年7月29日星期一

一种 Key-Value 的数据表扩展字段 bizExtInfo 的存取方式

1. 背景

当在数据库中设计表结构时,通常会预留个 扩展字段(biz_ext_info),防止后续因需求变化导致模型频繁变更的问题,这个 扩展字段(biz_ext_info) 通常会设置较大的可变字符串,如下:

CREATE TABLE `xxxxxx` (
  ...
  ...
  `biz_ext_info` varchar(2000) DEFAULT NULL COMMENT '扩展信息',
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='xxxxxx'
;

大部分人的做法会存储 JSON 字符串,存取时都会有个 JSON 解析的过程,同时数据显示也并不直观,下面是通过一种方式让 扩展字段(biz_ext_info) 里存储的数据以一种 Key-Value 的方式存储。

biz_ext_info

如上图所示,当查看 bizExtInfo 时,就类似于表字段+值的方式,相对于 JSON字符串 在查看或者读取使用时将会方便很多。

2. 实现

主要通过两个方法实现:

  • public static String map2string(Map<String, String> map)
  • public static Map<String, String> string2map(String str)

2.1. 存储

在保存数据库对象,这里以 Record 举例,使用 map2string 方法,将 map 转成 string 存储到数据库中

Map<String, String> map = Maps.newHashMap();
map.put("FLAG", "1312");
map.put("AllowPAY", "CHB");
record.setBizExtInfo(MapUtil.map2string(map));

2.2. 读取

从数据库中读取出来,使用 string2map 转成 map,由于 Record 对象里 bizExtInfo 属性定义的是字符串类型,通常会在 Record 类扩展一个方法,如 fetchBizExtInfo(String key)

public class Record {
    ......
    ......
    private String bizExtInfo;

    public String fetchBizExtInfo(String key) {
        Map<String, String> bizExtInfoMap = MapUtil.string2map(bizExtInfo);
        return bizExtInfoMap.get(key);
    }
}

3. 源码

2024年4月17日星期三

如何使用 Python rich 库让你的终端输出更加丰富多彩

python_rich.webp

Python 的 rich 库是一个用于在终端中创建富文本和美观格式的库。它可以用来打印彩色文本、表格、进度条等,使得命令行界面更加生动和用户友好。

以下是如何安装和使用 rich 库的一些基础示例。

1. 使用 pip 安装

$ pip install rich

确保你的 Python 版本至少是 3.6.1,这是 rich 库所要求的最低版本

2. 基本使用

下面列出基本常规的使用案例,rich 功能不局限于此,更多如渲染 markdowntree 或 输出代码语法高亮等功能,可参考 官方文档

2.1. 打印彩色文本

from rich import print

print("[bold red]Hello[/bold red], [green]World![/green]")

python_rich_color_text.webp

这个功能类似于 shell 中定义颜色文本输出,可参看 Shell技巧#终端使用色彩字体

2.2. 创建表格

酷炫的功能,终端可以使用表格展示数据,实用性很强

from rich.table import Table
from rich.console import Console

console = Console()

table = Table(show_header=True, header_style="bold blue")
table.add_column("Date", style="dim", width=12)
table.add_column("Title")
table.add_column("Production Budget", justify="right")
table.add_column("Box Office", justify="right")

table.add_row("Dec 20, 2019", "Star Wars: The Rise of Skywalker", "$275,000,000", "$375,126,118")
table.add_row("May 25, 2018", "[red]Solo[/red]: A Star Wars Story", "$275,000,000", "$393,151,347")
table.add_row("Dec 15, 2017", "Star Wars Ep. VIII: The Last Jedi", "$262,000,000", "$1,332,539,889")

console.print(table)

python_rich_table.webp

2.3. 进度条示例

from rich.progress import Progress
import time

with Progress() as progress:
    task1 = progress.add_task("[red]Downloading...", total=1000)
    task2 = progress.add_task("[green]Processing...", total=1000)
    task3 = progress.add_task("[cyan]Cooking...", total=1000)

    while not progress.finished:
        progress.update(task1, advance=5)
        progress.update(task2, advance=3)
        progress.update(task3, advance=9)
        time.sleep(0.02)  # 代表实际工作的代码

python_rich_progress.gif

2.4. 使用日志记录

from rich.logging import RichHandler
import logging

# 配置日志记录
logging.basicConfig(
    level="NOTSET",
    format="%(message)s",
    datefmt="[%X]",
    handlers=[RichHandler()]
)

logger = logging.getLogger("rich")

logger.info("这是一条信息日志")
logger.warning("警告!存在潜在问题")
logger.error("这是一条错误日志")

python_rich_log.webp

3. Rich cli

Rich 从 python 库引申了终端的 rich-cli 工具,安装(brew install rich)好可以直接在 shell 终端使用,几个比较使用的功能

$ rich README.md
$ rich data.json
$ rich notebook.ipynb
$ rich loop.py

Reference

2024年1月17日星期三

分享使用多年简单高效的 spring boot 模板工程结构

尽管目前市场主流编程语言有很多,但 Java 仍然是企业的使用首选,不仅是成长多年,积累了不少 Coder,也在于丰富的开源库,而 spring 全家桶仍然非常流行,本篇文章结合作者多年使用,总结的一个简单的 spring boot 模板结构,具有以下优缺点:

优点:

  • 干净清爽,不依赖任何第三方库,适合手工码字人
  • 一个的业务处理入口(Controller),避免入口泛滥在应用中的各个地方
  • 异常处理、响应结果、摘要日志、鉴权部分的预留等等,同样是避免切面膨胀
  • 研发只需关注业务部分代码的开发,不需要管异常、日志、鉴权等非主流程处理的代码
  • 自定义空间大,模板结构保存很多预留位,可自定义扩展

缺点:

  • 适用于处理通用HTTP业务请求,JSON格式的请求响应体,对于websocket或上传下载等需另行定义处理
  • 业务请求的配置路径在 xml 文件中完成,多人开发容易造成代码冲突

1. 类结构图

1.1. 领域对象

下图主要是领域对象的定义,涵盖异常、错误码与服务结果,错误级别、错误码都是为业务异常服务,出现异常的情况,最终会以正常状态服务结果返回,而不是异常堆栈的直接抛出。

20240118101728

为了更快的创建出模板结构,上面数字代表创建顺序,详细的代码可直接点击查看:

  • CommonResultCode,通用的结果定义,用于约束返回内容
  • ErrorCodeConstants,错误码常量定义,应结合错误码规范,其中包含错误码前缀、错误级别与业务域的定义
  • CommonResultCodeEnum,错误码枚举类的定义,因实现了CommonResultCodeErrorCodeConstants,只需定义自己的四位错误码
  • ServiceResult,响应结果,贯穿整条业务请求,其中定义约束了返回结构,应遵循各自的前后端交互规范
  • AbstractBizException,抽象的业务异常对象,由自定义异常继承
  • CustomBizException,自定义的业务异常,需继承AbstractBizException
  • AssertUtils,断言工具类,与AbstractBizException绑定,业务开发中不必if...else...判断,使用此工具直接抛

完成上面领域对象的定义,在 applicationContext.xml 中配置个异常的 Bean 对象,具体可以看AssertUtils类注释,第一步完成。

<bean class="com.xxx.xxx.exception.AssertUtils">
    <property name="exceptionClassName" value="com.xxx.xxx.exception.CustomBizException"/>
</bean>

1.2. 请求委派

领域对象贯穿业务请求的生命周期,对于每个请求的的扭转委派,如下图定义:

20240118135016

尽管已经最小化,为了简单,仍然用了一些工具类的包,提前配置在 pom.xml 文件中,如下:

<!-- JSON格式处理 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.65</version>
</dependency>

<!-- 工具包 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.8.1</version>
</dependency>

<!-- 日志 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.10</version>
</dependency>

按上图步骤,依次创建的类如下:

至此所有模板代码已创建完成,下面看如何使用。

2. 模板使用

2.1. 编写服务

DelUser,继承AbstractServiceCallback类,必需要实现executeService方法,beforeServiceafterService 可选

import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;

@Component("deleteUser")
public class DelUser extends AbstractServiceCallback<ServiceRequest, ServiceResult<String>> {

    /**
     * 前置处理(可选的方法),生命周期在 executeService 执行之前
     * 可以做一些参数校验等操作
     *
     * @param request 服务请求对象
     */
    @Override
    public void beforeService(ServiceRequest request) {
        super.beforeService(request);

        // 参数验证
        JSONObject postJson = request.parseData(JSONObject.class);
        AssertUtils.notNull(postJson, CommonResultCodeEnum.PARAM_ILLEGAL, "post data is null");

        Long id = postJson.getLong("id");
        AssertUtils.notNull(id, CommonResultCodeEnum.PARAM_ILLEGAL, "id is null");
    }

    @Override
    public ServiceResult executeService(ServiceRequest request) {
        // 获取返回对象,最终将在 result 的 T data 中呈现
        String resultData = "ok";

        return ServiceResult.valueOfSuccess(resultData);
    }

    /**
     * 后置处理(可选的方法),生命周期在 executeService 执行完后
     * 可以做回收的操作
     *
     * @param request 服务请求对象
     * @param result {@link ServiceResult}
     */
    @Override
    public void afterService(ServiceRequest request, ServiceResult result) {
        super.afterService(request, result);
    }
}

2.2. 注册服务

将上面的服务注册到request-mapping.xml文件中,下面的 entry key 就是要请求的路径,如/api/user/delete

<bean id="domainServiceProcess" class="com.cainiao.moore.template.DomainServiceProcessImpl">
    <property name="serviceMap">
        <map>
            <entry key="api_user_delete"  value-ref="deleteUser"/>
        </map>
    </property>
</bean>

2.3. 服务访问

启动 spring boot 应用, 使用 curl 或者 postman 这样的工具发起请求

20240118145639

3. Conclusion

整个服务模板,搭建起来废点事,但实际使用中,会发现非常简单,继承服务回调类,注册服务,技术只需要在自己的服务中编写业务代码,完全不需要关注日志怎么打,异常怎么处理,上层都已进行了封装。

仁者见仁,智者见智,适合自己的才是最好的

4. 代码及素材

Retrieving Secret Values Using REST Services in Infisical

Infisical is the open source secret management platform that developers use to centralize their application configuration and secrets like...