Skip to content

一、框架使用

运行环境:

  • JDK21 及以上

  • MySQL8 及以上

代码结构:

bash
{{PROJECT_NAME}}/
├─ pom.xml                # maven配置
├─ src/main/java/com/yimiyisu/{{PROJECT_NAME}}
  ├─ Application.java                    # 启动入口
  ├─ controller/                         # 接口层(参数校验、权限、路由)
  ├─ service/                            # 业务层(核心流程编排)
  ├─ domain/                          # 业务实体/DTO/枚举
  └─ xxxService.java                  # 领域服务
  ├─ events/                             # 事件层(异步任务、定时任务)
  ├─ model/                           # 事件模型
  ├─ stat/                            # 统计相关事件
  └─ xxxEvent.java/                   # 事件
  ├─ hooks/                              # 数据钩子(前后置逻辑)
  ├─ kit/                                # 工具
  └─ domain/                             # 跨层通用模型
└─ src/main/resources/
   ├─ app.properties                      # 环境配置
   └─ static/                             # 静态资源

1. pom.xml 初始模版

xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://maven.apache.org/POM/4.0.0"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>{{PROJECT_NAME}}</artifactId>
    <packaging>jar</packaging>
    <name>{{PROJECT_NAME}}</name>
    <groupId>com.yimiyisu</groupId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- 框架依赖 -->
        <dependency>
            <groupId>me.zeto</groupId>
            <artifactId>core</artifactId>
            <version>0.1.5-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.7.1</version>
                <configuration>
                    <appendAssemblyId>false</appendAssemblyId>
                    <descriptors>
                        <descriptor>package.xml</descriptor>
                    </descriptors>
                    <outputDirectory>${project.build.directory}/dist/</outputDirectory>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>me.zeto</groupId>
                <artifactId>plugin</artifactId>
                <version>0.1.6</version>
                <executions>
                    <execution>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>ZenOptimizer</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.6.0</version>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <minimizeJar>true</minimizeJar>
                            <createDependencyReducedPom>false</createDependencyReducedPom>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                        <exclude>META-INF/LICENSE</exclude>
                                        <exclude>META-INF/NOTICE</exclude>
                                        <exclude>META-INF/maven/**</exclude>
                                        <exclude>**/test/**</exclude>
                                        <exclude>**/about.html</exclude>
                                        <exclude>**/*.lombok</exclude>
                                        <exclude>lombok/**</exclude>
                                        <exclude>junit/**</exclude>
                                        <exclude>com/mongodb/**</exclude>
                                        <exclude>org/mongodb/**</exclude>
                                    </excludes>
                                </filter>
                                <filter>
                                    <artifact>com.mysql:mysql-connector-j</artifact>
                                    <includes>
                                        <include>com/mysql/cj/**</include>
                                    </includes>
                                </filter>
                            </filters>
                            <transformers>
                                <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.yimiyisu.{{PROJECT_NAME}}.Application</mainClass>
                                </transformer>
                            </transformers>
                            <finalName>${project.name}</finalName>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

注意:<minimizeJar>true</minimizeJar>开启了包压缩,打包后可能会导致某些依赖丢失,需要手动排出,如:

xml
<filter>
    <artifact>com.mysql:mysql-connector-j</artifact>
    <includes>
        <include>com/mysql/cj/**</include>
    </includes>
</filter>

2. Application.java

初始模版:

java
public class Application extends ZenApp {

    public static void main(String[] args) {
        int listenPort = 7058;
        String appName = "interact";
        ZenApp app = new Application();
        app.start(args, appName, listenPort);
    }
}

2.1 必须 extends ZenApp

2.2 listenPort 为端口号

2.3 appName为应用名

2.4 若为多店铺系统的店铺端,需要切换店铺功能,需添加

java
app.multiTenant();

配合 ZenTenantHook 一起使用

2.5 若需要操作日志,需添加

java
app.openBizLoger(new OperationLogService());

参数为自己实现的service,service示例:

typescript
public class OperationLogService implements IBizLogger {

    @Override
    public void put(BizLogDO bizLogDO) {
        AliLogService.setLog(bizLogDO);
    }
}

必须 implements IBizLogger,此时当数据库配置了启用日志时,对该数据库的操作会被该方法拦截

3. controller 类

例:

java
@Slf4j
@AccessRole(ZenRole.ANONYMITY)
public class Home extends ZenController {
    @Inject
    private ZenEngine zenEngine;
    @Inject
    private WxAuthService wxAuthService;
    
}

2.1 @AccessRole(ZenRole.ANONYMITY):访问权限控制,必须有

ZenRole枚举:

ANONYMITY匿名用户
SIGNATUREC 端登陆用户
ADMINC 端管理员
CONSOLE中后台登录用户
SUPPER中后台管理员用户
DEVELOPER研发用户

2.2 extends ZenController:必须继承ZenController

2.3 @Inject:注入 Bean 对象

4. controller 中方法

例:

java
@MethodType(ZenMethod.ALL)
    @Tracker(1)
    public ZenResult test(ZenData data) {
        MessageService messageService = ConfigKit.getBean(MessageService.class);
        System.out.println(messageService);
        try (ExceptionTracker tracker = new ExceptionTracker("test")) {
            tracker.setTitle("好像有异常");
            return ZenResult.success();
        }
    }

3.1 @MethodType(ZenMethod.ALL):声明访问类型,默认 POST,可填 GET,ALL,尽量都标明

3.2 @Tracker(1):跟踪接口执行时间,上下文参数,计时器默认值 200ms,接口执行超过这个时间会在日志中打印出来

3.3 ZenResult:返回值,固定

3.3.1 属性说明

success请求接口是否成功
message响应的用户消息
action原始的请求对象
data响应的数据

3.3.2 常用方法

ZenResult success()返回成功结果
ZenResult success(String message)返回成功结果,带输出消息
ZenResult success(String message, ZenAction action)返回成功结果,带消息和前端指令
ZenResult fail(String message)返回失败结果,带异常消息
ZenResult fail(Throwable throwable)返回失败结果,带异常盏
ZenResult redirect(String uri)服务器端二次跳转
ZenResult parse(String content)将字符串结果集,转成对象
boolean isEmpty()判断接口返回结果是否为空
ZenResult refresh()通知前端刷新页面
ZenResult setData(Object data)设置结果数据
boolean exist(String key)判断返回对象是否包指定字段
String get(String key)获取返回对象指定字段值
T asEntity(Class<T> clazz)单条数据接口返回实例化对象
PageEntity<T> asPageEntity(Class<T> clazz)分页接口返回实例化的分页对象
List<T> asList(Class<T> clazz)列表接口返回实例化的列表对象
List<String> getAsStringList(String key)获取对象指定字段的列表字符串
List<Map<String, Object>> asListMap()获取 list 的 map 对象
T get(String key, Class<T> clazz)指定字段转成实例化对象
String[] getList(String name)获取字符串列表
int getInt()count 接口返回整形对象
int getLong()sum 接口返回长整形对象

ZenAction 输出行为对象

SUCCESS()请求成功
REBACK()返回到上一页
JUMP()客户端跳转,data 为目标地址
REFRESH()刷新客户端网页
GOLOGIN()跳转到登录页面
E404()跳转到 404 页面
END()程序结束,中断后续逻辑,hook 拦截时使用
ORIGINAL()输出原生字符串
NOPERMISSION()无访问权限
FAIL()请求失败
REDIRECT()页面302重定向

使用示例

java
return ZenResult.success().setAction(ZenAction.REDIRECT).setData(authorizeUrl);

3.4 ZenData:接口请求的入参对象,固定 待补充

常用方法

ZenData create()静态函数构造 ZenData
ZenData create(ZenData data)静态函数构造 ZenData,继承 data 的上下文信息
boolean isEmpty()判断字段是否为空
boolean isEmpty(String key)判断字段 key 是否为空
String get(String name)获取 key 字段数据
String query(String name)获取 url 中的 queryString 参数值
String getHeader(String headerName)获取用户请求的 header 值
String getIP()获取用户请求的真实 IP
T get(String key, Class<T> clazz)将 key 字段数据转成 clazz 型对象
List<Map<String, Object>> getAsListMap(String key)将 key 字段数据转成 List 的 map 对象
List<T> getAsList(String key, Class<T> clazz)将 key 字段数据转成 clazz 的列表对象
String getTenant()获取租户 ID
Session session()获取用户的 Session 对象
ZenData put(String key, Object value)增加新数据
ZenData batch(JsonElement element)批量插入数据
T parse(Class<T> clazz)转成业务对象
int getPageNumber()获取分页的页号,默认值 0
int getPageSize()获取分页的 size, 默认值 20,最大 100
String[] getKeys()获取参数的所有 key
String getAccount()获取主账户
ZenUser getUser()获取user
String getUid()获取uid

5. service 类

用@Component注册,用@Inject引入其他service

6. domain 实体类

系统已经集成lombok,@Data尽量所有实体类都加上

7. ZenEngine 数据操作引擎

用来进行所有数据库操作

6.1 使用方法

java
@Inject
private ZenEngine zenEngine;

6.2 常用方法

示例:

java
ZenResult groupResult = zenEngine.execute("get/white_group", ZenData.create("id", integralWhite.getGroup()));
ZenResult execute(String api, ZenData zenData)执行自动化接口
void inc(String table, String id, String column, int count)单字段数据自增,自减操作
List<JsonObject> selectById(String tableName, List<String> ids)手工批量查询

6.3 跨应用数据库操作

表名格式为数据库实际表名,格式:appName_tableName,zen 前缀的系统表禁止处理

JsonObject get(String tableName, String id)单条数据查询
JsonObject get(String tableName, Map<String, String> condition)多条件单条数据查询
void update(String tableName, String id, Map<String, Object> data)更新单条数据
void update(String tableName, Map<String, Object> data, Map<String, String> condition)多条件更新单条数据
void delete(String tableName, String id)删除单条数据

8. event 事件

7.1 先定义model

命名格式为 xxxEventModel,定时任务为xxxLoopEventModel

示例:

java
@Data
public class RecordExportEventModel {
    private String encryptKey; //加密密码
    private int encryptType; //加密类型 0 不加密 1 自动加密 2 人工加密
}

7.2 编写event类

命名格式对应model名,为xxxEvent,定时任务为xxxLoopEventModel,xxx和model保持一致

示例:

java
TypeScript
public class RecordExportEvent implements IEvent<RecordExportEventModel> {
    @Inject
    private RecordExportService recordExportService;
    @Inject
    private ZenEngine zenEngine;
    
    @Override
    @Subscribe
    public void execute(RecordExportEventModel recordExportEventModel) {
        if (CacheKit.has("recordExportEventModel:"+recordExportEventModel.getExportTaskId())) return;
        CacheKit.set("recordExportEventModel:"+recordExportEventModel.getExportTaskId(), 1, 1);
        String cdnPath = recordExportService.RecordExport(recordExportEventModel);
        String exportTaskId = recordExportEventModel.getExportTaskId(); // 导出任务id

        if (!cdnPath.isEmpty()){
            // 回填path并修改状态为成功
            zenEngine.execute("patch/export_task", ZenData.create().put("id", exportTaskId).put("status", 2).put("path", cdnPath));
        }else{
            // 更新状态为:导出失败
            zenEngine.execute("patch/export_task", ZenData.create().put("id", exportTaskId).put("status", 3));
        }
    }
}
  1. 必须实现IEvent<对应的model类名>

  2. 重写execute方法,必须添加@Override @Subscribe 两个注解,方法的参数为对应的model类

  1. 定时任务需要在类上添加@Crontab()注解,里面写时间表达式,如"0 */1 * * * ?",需要查数据时,必须使用游标模式,通过id作为游标,对应的查询接口要以id升序排序,条件要有id > 传入的id这个条件

示例:

java
Java
/**
 * 定时补发 每两分钟执行一次
 */
@Crontab("0 */1 * * * ?")
@Slf4j
public class ReissueLoopEvent implements IEvent<ReissueLoopEventModel> {

    @Inject
    private ZenEngine zenEngine;
    private static final String RECORD_CURSOR = "reissue_loop_cursor";

    @Override
    @Subscribe
    public void execute(ReissueLoopEventModel reissueLoopEventModel) {
        // 从缓存获取游标
        String cursor = CacheKit.get(RECORD_CURSOR); // 上次执行到的中奖记录id

        long now = DateKit.now();
        // 60分钟以上 白天:6h之内 晚上:30天之内
        long begin = DateKit.isNight() ? now - 30 * 24 * 3600 : now - 6 * 3600;
        ZenData param = ZenData.create().put("status", 1).put("pageSize", 100).put("id", cursor).put("createGmt", now)
                .put("updateGmt", DateKit.now() - 60 * 60);
        ZenResult recordResult = zenEngine.execute("list/activity_record_cursor_send", param);
        if (recordResult.isEmpty()) {
            // 延时重置游标
            EventKit.trigger(cursor, 20, (item) -> {
                String id = String.valueOf(item);
                if (StringKit.isNotEmpty(id) && id.equals(CacheKit.get(RECORD_CURSOR))) {
                    // System.out.println("重置发放中游标" + id);
                    CacheKit.set(RECORD_CURSOR, StringKit.objectId(begin), 5);
                }
            });
            return;
        }

        List<SendRewardEventModel> recordResultList = recordResult.asList(SendRewardEventModel.class);
        // 将最后一条记录id存入缓存
        CacheKit.set(RECORD_CURSOR, recordResultList.getLast().getId(), 5);

        // 执行发送 - 补发
        for (SendRewardEventModel record : recordResultList) {
            // 中奖记录大于30天的跳过
            if (DateKit.now() - record.getCreateGmt() > 3600 * 24 * 30)
                continue;
            // 补发更新成待发放
            zenEngine.execute("patch/activity_record_status", ZenData.create("id", record.getId()).put("status1", "1")
                    .put("status2", "2").put("createGmt", DateKit.now()));
            EventKit.trigger(record);
        }
    }
}

7.3 事件触发

定时任务无需手动触发

异步任务需要手动触发,通过EventKit

EventKit 常用方法

trigger(Object eventModel)触发对应 model 的异步事件
trigger(Object eventModel, int seconds)延时 seconds 秒后触发对应 model 的异步事件
trigger(Object data, int seconds, Consumer<Object> callback)延时 seconds 秒后触发函数调用
off(Object eventModel)卸载异步事件

示例:

java
Java
// 异步扣除积分明细
PointsExpenseEventModel pointsExpenseEventModel = new PointsExpenseEventModel();
pointsExpenseEventModel.setId(resultPut.get("id"));
pointsExpenseEventModel.setPointsId(pointsId);
pointsExpenseEventModel.setTotal(num);
pointsExpenseEventModel.setPurpose(purpose);
pointsExpenseEventModel.setExtra(extra);
EventKit.trigger(pointsExpenseEventModel);

事件中execute方法的参数即为触发时传入的参数

9. hooks

可以拦截/api接口和/do接口,在接口执行前或后做一些操作,尽量不使用

示例:

java
Java
@ZenHook({"patch/templateWithDefault"})
public class PathTypeHook implements IHook {
    @Inject
    private ZenEngine zenEngine;

    @Override
    public ZenResult before(ZenData data) {
        IHook.super.before(data);
        return zenEngine.execute("patch/templateNoDefault", ZenData.create());
    }
}
  1. 必须添加ZenHook注解,接口可以写多个

  2. 必须 implements IHook

  1. 重写接口中方法,共有两个

before:在方法执行前拦截,返回 ZenResult 对象,可以通过 ZenAction.End 终止方法继续执行

入参:ZenData 出参:ZenResult

after:在方法执行后,调用的钩子,可用于异步调用,MQ 消息发送等

入参:ZenData ZenResult 出参:无

  1. 若为多租户系统后台,需要切换租户时,必须添加 ZenTenantHook,返回当前用户有哪些租户

示例:

java
TypeScript
@ZenHook("zenTenant")   // 固定
public class ZenTenantHook implements IHook {

    @Inject
    private ZenEngine zenEngine;

    @Override
    public ZenResult before(ZenData data) {
        if(StringKit.isEmpty(data.getAccount())) return ZenResult.success().setAction(ZenAction.END);
        ZenResult tenantResult = zenEngine.execute("list/stall_employ", data);
        if (tenantResult.isEmpty()) {
            return ZenResult.fail("找不到有效门店");
        }

        List<String> list = tenantResult.asList().stream().
                map((element) -> element.get("stall").getAsString()).toList();

        return ZenResult.success().
                put("list", list).
                put("depend", "stall").
                setAction(ZenAction.END);
    }
}

10. app.properties 配置文件

9.1 运行环境

java
env=LOCAL_DEV

配置名 env

值 LOCAL_DEV:本地 daily:日常 demo:演示 online:线上

9.2 数据库配置

java
dbHost=_$$UdCez9vGORbBEKP1zpArpnJo7YyRtdLSMbk7cOwnX90k5b5D8owOVh1lNv/xPhYF
dbUser=_$$egPUAfl+Hf7P6uyCLpHyzg==
dbPasswd=_$$NymQvkQqV0S3FrBEIwByjkzd+azx0+z73VwFajUZhmo=

如果zen_user等系统表和业务表不在一个数据时,需单独配置
zenDbPasswd=
zenDbUser=
zenDbHost=

9.3 redis配置

java
redisAddress=redis://:74edsCG9bWpmSBOn@db.zeto.me:6021/10

9.4 自定义配置

自定义的配置,程序中通过ConfigKit获取

示例:ConfigKit.get("wxMiniAppId")

常用方法

String get(String name)获取指定配置
JsonObject selfWidthTenant(String tenantId, String name)获取租户的自定义配置
String get(String name, String defaultValue)获取配置,设置默认值
JsonObject self(String name)获取自定义配置
JsonObject self(String name)获取自定义配置
JsonArray selfAsArray(String name)获取数组类型配置
void self(String name, String data)设置自定义配置
void selfWidthTenant(String tenantId, String name, String data)设置租户级别的自定义配置
String getPath()当前环境物理路径
BeeDataSource getDatasource()获取数据库的数据源,用于自定义 mybatis 配置等
boolean isDev()是否开发环境
boolean isDaily()是否日常环境
boolean isOnline()是否线上环境
void setTenantExpire(String tenantId, boolean status)设置租户是否过期,true 过期,false 未过期

11. 广播 Broadcast

10.1 配置文件配置

  • broadRedis : 默认广播服务器与缓存服务相同,可以通过该变量指定服务

  • boradTenant : 指定监听的租户对象

10.2 对象属性

String target广播目标对象,一般取对象 Id
String type广播的业务类型
String tenantId租户ID,非必填

10.3 常用方法

Broadcast(String target, String type)构造函数
Broadcast(String target, String type, String tenantId)构造函数
Broadcast every()启用多实例接收,默认单实例接收
void send(String app)输入目标应用,发送广播
void send(String app, Object data)输入目标应用和广播的数据,发送广播
String getData()获取广播数据,只能获取一次

10.4 使用示例

使用前要确保两个应用广播服务器相同

营销平台通知商城发积分

营销平台写法:

java
Broadcast broadcast = new Broadcast(record.getId(), "mall_points", expand.getShopId());
broadcast.send("mall_client");

因为商城有多个环境,所以要指定租户,因为每个商城的店铺id不同,所以本例使用店铺id,对应商城应用配置文件要配置boradTenant=店铺id

商城接收广播写法:

先在event目录下创建broadcast目录,在目录中创建BroadcastEvent.java,以上名称固定

typescript
// 监听广播
public class BroadcastEvent implements IEvent<Broadcast> {
    @Inject
    private PointsSendService pointsSendService;

    @Override
    @Subscribe
    public void execute(Broadcast broadcast) {
        System.out.println("收到广播"+broadcast.getType()+broadcast.getTarget());
        switch (broadcast.getType()){
            case "mall_points":
                pointsSendService.send(broadcast.getTarget());
                return;
        }
    }
}

IEvent<Broadcast> 固定,如需接收其他广播,添加对应的case即可

12. 大屏/统计数据 DataV

DataV 对象属性:

String id统计编号
String targetId统计对象,非必填
boolean refreshing是否刷新中

DataV 对象方法:

static DataV get(String datavId)获取统计对象
static DataV get(String targetId, String datavId)获取统计对象
void setCacheTime(int time=600)数据缓存时间,默认 600 秒
DataV put(String key, Object value)添加统计属性
void refresh()主动刷新统计
void save()保存统计结果

使用示例:

  1. 前端使用方法,调用对应的获取接口,如果需要刷新,就延迟重新执行
javascript
async getData() {
    this.data = await $.post({
        url: "/api/member/homeStat",
    });
    if (this.data.isRefreshing) {
        // 延迟2秒后重新执行
        await new Promise((resolve) => setTimeout(resolve, 2000));
        return await this.getData();
    }
    return this.data;
},
  • Controller 写法
java
/**
 * 获取大屏数据
 */
public ZenResult homeStat(ZenData data) {
    String account = data.getAccount();
    // 首页账户数据
    DataV accountData = DataV.get(data.getAccount(), AccountHomeStat.ID);
    // 首页门店数据
    DataV stallData = DataV.get(data.getTenant(), StallHomeStat.ID);
    boolean isRefreshing = accountData.isRefreshing() || stallData.isRefreshing();
    return ZenResult.success().put("accountData",accountData.getResult()).put("stallData",stallData.getResult()).put("isRefreshing",isRefreshing);
}
  • 在events目录下创建stat目录,编写统计类

例:

java
// erp首页账户数据
@Component
public class AccountHomeStat implements IStat {

    public final static String ID = "erp:accountHomeStat";

    @Inject
    private SettingsStat settingsStat;
    @Inject
    private StockStat stockStat;

    @Override
    public void execute(DataV dataV) {
        // 基于账户统计
        String accountId = dataV.getTargetId();
        //获取客户总数
        long customerNumber = settingsStat.getCustomerNumber(accountId);
        //获取供应商总数
        long supplierNumber = settingsStat.getSupplierNumber(accountId);
        //获取库存总数
        long stockNumber = stockStat.getStockNumber(accountId);
        //获取库存总额
        long stockAmount = stockStat.getStockAmount(accountId);
        //获取库存预警数
        long stockWarning = stockStat.getStockWarning(accountId);
        //获取客户欠款
        long customerDebt = settingsStat.getCustomerDebt(accountId);
        //获取供应商欠款
        long supplierDebt = settingsStat.getSupplierDebt(accountId);
        //获取过期商品数量
        long expiredNumber = stockStat.getExpiredNumber(accountId);
        dataV.put("customerNumber", customerNumber);
        dataV.put("supplierNumber", supplierNumber);
        dataV.put("stockNumber", stockNumber);
        dataV.put("stockAmount", stockAmount);
        dataV.put("stockWarning", stockWarning);
        dataV.put("customerDebt", customerDebt);
        dataV.put("supplierDebt", supplierDebt);
        dataV.put("expiredNumber", expiredNumber);
        dataV.setCacheTime(600);
        dataV.save();
    }
}
  • 在stat目录下创建tools目录,放settingsStat、stockStat等java类

例:

typescript
@Component
public class SettingsStat {

    @Inject
    private ZenEngine zenEngine;

    //获取客户总数
    public long getCustomerNumber(String account) {
        return zenEngine.execute("count/customer_total", ZenData.create("account", account)).getLong();
    }

    //获取供应商总数
    public long getSupplierNumber(String account) {
        return zenEngine.execute("count/supplier_total", ZenData.create("account", account)).getLong();
    }

    //获取客户欠款
    public long getCustomerDebt(String account) {
        return zenEngine.execute("sum/customer", ZenData.create("account", account)).getInt("receiveDebt");
    }

    //获取供应商欠款
    public long getSupplierDebt(String account) {
       return zenEngine.execute("sum/supplier", ZenData.create("account", account)).getInt("payDebt");
    }

    //获取欠款客户数
    public long getDebtCustomerNumber(String account) {
        return zenEngine.execute("count/customer", ZenData.create("account", account)).getLong();
    }

    //获取欠款供应商数
    public long getDebtSupplierNumber(String account) {
        return zenEngine.execute("count/supplier", ZenData.create("account", account)).getLong();
    }
}

13. 内置Kit

1. StringKit 字符串工具类

String objectId()生成一个对象的标准雪花 ID
String shortId()生成一个对象的短雪花 ID
String objectId(long time)指定时间生成雪花 ID
String shortId(long time)知道时间生成短雪花 ID
boolean isEmpty(String str)是否空字符串
boolean isEmpty(Object str)是否空对象
boolean isNotEmpty(String... str)非空字符串
boolean isNumber(String value)是否是数字
String get(String name)获取指定配置
String fileExt(String fname)获取文件后缀
int rand(int min, int max)生成一个随机整数
String rand(int size)生成长度为 size 的随机数字字符串
String md5(String words)生成 MD5 的密纹字符串,不能用于密码
String SHA1(String words)生成 SHA1 的密纹
String SHA512(String words)生成 SHA512 的密纹
String SHA256(String words)生成 SHA256 的密纹
String urlEncode(String content)用 utf-8 字符集,进行 URL 编码
String encrypt(String content)基于当前时间戳(秒)生成密文
DecryptResult decrypt(content content)将时间戳密文解密为明文和原时间
String encrypt(String content, String ukey)AES 加密后转为 Base64 编码
String decrypt(String content, String ukey)Base64 解码后 AES 解密

13.2 CacheKit 缓存工具类

  • 缓存默认时长 180 分钟,所有计数器类缓存为持久缓存无时长限制
String get(String cacheId)获取字符串型缓存
T get(String cacheId, Class<T> clazz)获取指定类型的对象
String getWithClean(String cacheId)获取缓存后删除缓存,常见:验证码场景
void set(String cacheId, Object data)设置缓存,有效时长 180 分钟
void set(String cacheId, Object data, Integer minutes)指定时长,设置缓存,0 表示永不过期
bool has(String cacheId)判断缓存是否存在
void remove(String cacheId)删除缓存
int hGet(String cacheId, String prop)获取哈希对象计数器值
String hGetObject(String cacheId, String prop)获取 hash 缓存的对象值
void hSet(String cacheId,String prop, Object value)对象类型 hash 缓存,永久有效
void hSet(String cacheId, Map<String, Integer> data)批量计数器的值同步,注意不是增减,是覆盖式原数据
void hSet(String cacheId, String prop, int number)单条计数器的值同步,会覆盖原数据
long hIncrBy(String cacheId,String prop, int number)增减原子计数器,number 正值为增加,负值为减少
Set<String> hKeys(String cacheId)获取 hash 缓存的所有 key
void hDel(String cacheId,String prop)根据 hash 的 key,删除对应缓存

13.3 ConfigKit 配置中心工具类

String get(String name)获取指定配置
JsonObject selfWidthTenant(String tenantId, String name)获取租户的自定义配置
String get(String name, String defaultValue)获取配置,设置默认值
JsonObject self(String name)获取自定义配置
JsonObject self(String name)获取自定义配置
JsonArray selfAsArray(String name)获取数组类型配置
void self(String name, String data)设置自定义配置
void selfWidthTenant(String tenantId, String name, String data)设置租户级别的自定义配置
String getPath()当前环境物理路径
BeeDataSource getDatasource()获取数据库的数据源,用于自定义 mybatis 配置等
boolean isDev()是否开发环境
boolean isDaily()是否日常环境
boolean isOnline()是否线上环境
void setTenantExpire(String tenantId, boolean status)设置租户是否过期,true 过期,false 未过期

13.4 DateKit 日期工具类

long now()当前 unix 时间戳
boolean isNight()当前是否处于夜间23点至第二天6点以前
long today()今天 0 点的时间戳
long today(int span)以今天 0 点时间戳为基点,获取 span 天后的 0 点时间戳
long month()当前月的 0 点时间戳
long month(int month)指定月份的 0 点时间戳
long week()当前周的 0 点时间戳
Calendar getCalendar()获取当前 0 点的日期对象
boolean isInCronBetween(long time,String cronStart,String cronEnd)判断指定时间是否在,两个时间表达式之间
String toString(long date)unix 时间戳转长格式的日期字符串
String toShortString(long date)unix 时间戳转短格式的日期字符串
String toString(long unixTime, String pattern)指定格式,把 unix 时间戳转格式化后的日期字符串
String toString(Date date)指定时间转长格式的日期字符串
String toString(Date date, String pattern)指定时间转指定格式的日期字符串
long toUnix(String time, String pattern)指定时间转指定格式的日期字符串
long toUnix(String time)时间字符串转 unix 时间戳
String gmtDate()当前时间 GMT 格式的字符串
String gmtDate(LocalDateTime localDateTime)指定时间 GMT 格式的字符串
String gmtDate(Date date)指定时间 GMT 格式的字符串

13.5 EventKit 异步事件工具类

trigger(Object eventModel)触发对应 model 的异步事件
trigger(Object eventModel, int seconds)延时 seconds 秒后触发对应 model 的异步事件
trigger(Object data, int seconds, Consumer<Object> callback)延时 seconds 秒后触发函数调用
off(Object eventModel)卸载异步事件

13.6 HttpKit 网络请求工具类

  • 尽量多使用异步调用外部接口,如果上游服务限流了,可通过轮询事件控制请求频率
String get(String url)请求 Get 接口,返回文本内容
String get(String url, Consumer<String> callback)异步请求 Get 接口,返回文本内容
T get(String url, Class<T> clazz)请求 Get 接口,返回指定类型对象
String get(String url, Map<String, Object> params)带参数请求 Get 接口,返回文本内容
void get(String url, Map<String, Object> params, Consumer<String> callback)异步带参数请求 Get 接口,返回文本内容
T get(String url, Map<String, Object> params, Class<T> clazz)带参数请求 Get 接口,返回指定类型对象
Map<String, Object> getAsMap(String url)请求 Get 接口,返回 Map 对象
Map<String, Object> getAsMap(String url, Map<String, Object> params)带参数请求 Get 接口,返回 Map 对象
String post(String url, Map<String, Object> data)请求 Post 接口,返回文本内容
void post(String url, Map<String, Object> data, Consumer<String> callback)异步请求 Post 接口,返回文本内容
T post(String url, Map<String, Object> params, Class<T> clazz)请求 Post 接口,返回指定类型对象
Map<String, Object> postAsMap(String url, Map<String, Object> data)请求 Post 接口,返回 Map 对象
String postAsForm(String url, Map<String, Object> data)传统 Form 表单形式,发送 Post 请求
void postAsForm(String url, Map<String, Object> data, Consumer<String> callback)异步传统 Form 表单形式,发送 Post 请求
void postFile(String url,String fileName, String filePath, Map<String, String> data, Map<String, String> headers, Consumer<String> callback)异步传统 Form 表单形式,上传文件 Post 请求
InputStream download(String url)发起 Get 请求,下载文件流

13.7 JsonKit Json工具类

T parse(String json, Class<T> clazz)字符串转指定类型对象
String stringify(Object value)对象序列化为字符串
T parse(Object json, Class<T> clazz)转指定类型对象
Map<String, Object> parseAsMap(String json)转成 map 对象
List<Map<String, Object>> parseAsListMap(String json)转成 list 集合的 map 对象
Map<String, String> parseAsStringMap(String json)转成字符串型 map 对象
List<String> parseAsStringList(String json)转成字符串的 list 集合
List<T> parseAsList(String json, Class<T> clazz)转成指定类型的 list 集合
JsonObject parse(String json)字符串转 jsonObject
JsonObject parse(Object json)对象转 jsonObject

13.8 UploadKit 上传工具类

String image(String url, String path)将网络图片,上传到服务器,并返回新路径
void put(String path, String content)将文本内容推送到云存储上
void put(String path, InputStream inputStream)将文本推送到私有云存储
String get(String path)获取文件内容
String getURL(String path)将附件路径,转换成临时授权后的可访问路径,有效期 10 分钟
void remove(String path)删除指定文件
void refresh(String path)刷新 CDN 文件缓存,由于 CDN 原理,不同节点间有 3-5 分钟延迟

13.9 UserKit 用户工具类

ZenUser get(String uid)通过 ID 查询用户
List<ZenUser> selectByUids(List<String> ids)通过 uid 数组批量查询用户
void pause(String uid)暂停用户登录系统
String insert(ZenUser user)系统中插入用户,返回新用户登录凭证 Token
void update(ZenUser user)更新用户头像,昵称,邮箱等信息
void setMeta(String uid, String name, String value)添加标记信息,标记信息是用户额外的缓存信息,不能存储业务数据
void setOpenId(String uid, String openId)设置用户 openId,如果已存在则不允许修改
void removeMeta(String uid, String name)删除标记信息
void update(ZenUser user)更新用户头像,昵称,邮箱等信息
boolean updateMobile(String uid, String mobile, UserTag tag)更新手机号,已存在的手机号不能更新
void updateUionId(String uionId, String uid)更新用户 uionId
void updateTag(ZenUser user)更新用户标记
void removeToken(String token)删除指定 Token
void cleanToken(String uid)清除用户所有 token
void cleanToken(int days)清除 N 天以前登陆的 token
String createToken(String uid)生成用户新 token
ZenUser getByToken(String token)根据 token 返回用户
String createToken(String uid, String qrCodeId)根据二维码 ID,跨端生成用户新 token
void changePassword(String uid, String pwd)修改用户密码
List<ZenUser> search(String keyword)根据关键字搜索用户
boolean exist(String username)判断用户名是否存在
boolean getByOpenId(String openId, String app)根据 openId 和应用名查询用户
ZenUser getByOpenId(String openId, UserTag tag)通过 openId 查询用户
getByName(String username)通过用户名查询用户
ZenUser getByMobile(String phone, ZenRole tag)手机号查询用户
ZenUser getByMobile(String phone, String app)手机号,所属应用查询用户
ZenUser getByMobile(String phone, int tag, String app)手机号,自定义标,所属应用查询用户

13.10 IOKit 文件工具类

String readToString(String file)读取文件文本内容
String readToString(BufferedReader bufferedReader)读取文件文本内容
String readToString(Path path)读取文件文本内容
String readToString(InputStream input)读取文件文本内容
void copyFile(File source, File dest)本地复制文件
void compressGZIP(File input, File output)gzip 压缩文件,输出到指定路径
byte[] compressGZIP(byte[] data)gzip 压缩字节码,返回新字节码
byte[] compressGZIP(String content)gzip 压缩文本为字节码,默认 utf-8 编码
byte[] compressGZIP(String content, Charset charset)gzip 指定编码压缩文本

13.11 TagKit 标签工具类

long getValue(int position)获取标记值
long remove(long value, int position)删除标记
boolean contain(long value, int position)是否包含标记
long add(long value, int position)添加标记
boolean isHas(long value, long data)是否包含指定值的标记

13.12 PatternKit 正则表达式工具类

boolean isStrongPassword(String password)是否是强类型密码
boolean isSnowId(String str)是否是对象雪花 ID
boolean isEmail(String email)是否邮箱
boolean isIdCard18(String idCard)是否 18 位身份证
boolean isIdCard15(String idCard)是否 15 位身份证
boolean isImage(String suffix)是否图片
boolean isMobile(String mobile)是否手机号
boolean isPhone(String phone)是否电话号码
boolean isDigit(String digit)是否数字字符串
boolean isDecimals(String decimals)是否浮点字符串
boolean isBlankSpace(String blankSpace)是否空格
boolean isChinese(String chinese)是否中文
boolean isNumber(String str)是否数字
boolean isNumberOrChar(String str)是否数字或英文字符串

13.13 ExceptionTracker 异常跟踪类,自动发送异常消息

  • 同类型异常,3 分钟内,发生 10 次,自动触发消息通知

  • 消息模板名在配置文件中设置,配置名称 exception

  • 消息声明需要在 kooteam 中设置

确定性异常:

java
ExceptionTracker.report("test", 10, 180, "系统发生异常了");

未知异常:

java
public ZenResult test(ZenData data) {
    try (ExceptionTracker tracker = new ExceptionTracker("test",10,180)) {
        tracker.setMessage("系统发生异常了");
        return ZenResult.success();
    }
}

13.14 TimeTracker 程序运行计时器

  • 程序运行超过指定时间,自动输出运行时长日志

  • 时间单位毫秒

示例:

java
try (TimeTracker timeTracker = new TimeTracker(url, 300)) {
    HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
    if (response.statusCode() == 200) return response.body();
    else System.out.printf("%s 调用异常 %n响应内容: %s", url, response.body());
} catch (IOException | InterruptedException e) {
    System.out.printf("%s 调用异常 %n响应内容: %s", url, e.getMessage());
}

14. ZenMessage 消息对象

15. ZenPay 通用支付

16. ZenAuth 开放授权登录