GQL GET 请求生成图片文件流 — 技术实现方案

GQL GET 请求生成图片文件流 — 技术实现方案

基于 Oinone/Pamirs GQL GET 文件流机制,参考 FileAction#downloadFormData(查询型)与 ExcelExportTaskAction#createExportTask(同步写流),实现「按业务 ID 动态生成图片并输出文件流」的通用指南。


1. 背景与目标

1.1 需求

在业务场景中,需要根据业务 ID(如订单 ID、模板 ID、单据 ID)在后端动态渲染图片(条码、标签、证书、预览图等),并将结果以文件流形式返回给前端。

1.2 约束

  • 复用 Oinone 既有鉴权、会话、模块路由体系,不新增独立 REST 下载接口
  • 与 Excel 同步导出保持一致的调用方式:通过 GET 访问 /pamirs/{module}?query=...&variables=...
  • 前端仅两种消费方式:
    • 页面预览<img :src="imageUrl" />
    • 触发下载window.open(imageUrl, '_blank')

2. 参考实现

2.1 查询型文件流(图片查看应对齐此模式)

图片查看属于只读查询,应使用 query + @Function(type = QUERY),与 CDN 文件下载一致:

// @oinone/kunlun-vue-ui-common/.../UploadService.ts
const gql = `query{resourceFileFormQuery{downloadFormData(resourceFileForm:{downloadUrl:"${url}"}){filename}}}`;
window.open(UrlHelper.appendBasePath(`/pamirs/file?query=${encodeURIComponent(gql)}`), '_blank');
// pamirs-core/pamirs-file2/.../FileAction.java
@Function.Advanced(displayName = "前端下载数据", type = FunctionTypeEnum.QUERY)
public ResourceFileForm downloadFormData(ResourceFileForm resourceFileForm) {
    // 直接写 HttpServletResponse 输出流
    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", "attachment; filename=" + ...);
    IOUtils.copy(inputStream, response.getOutputStream());
    return new ResourceFileForm();
}

图片查看用 GQL.query,不用 GQL.mutation

2.2 同步写流机制(共用)

无论 query 还是 mutation,文件流输出的底层机制相同:在 Function/Action 执行过程中直接写 HttpServletResponse

2.2.1 HTTP 网关:RequestController

GET  /pamirs/{moduleName}?query={gql}&variables={json}
POST /pamirs/{moduleName}  body: { query, variables }

GET 与 POST 走同一执行链路

// pamirs-framework/pamirs-gateways-graph-java/.../RequestController.java
@RequestMapping(value = "/pamirs/{moduleName}", method = RequestMethod.GET)
public DeferredResult<String> pamirsGet(
    @PathVariable("moduleName") String moduleName,
    @RequestParam("query") String gql,
    @RequestParam(value = "variables", required = false) String variables,
    ...) {
    PamirsClientRequestParam gqlRequest = new PamirsClientRequestParam();
    gqlRequest.setQuery(gql);
    if (!StringUtils.isBlank(variables)) {
        gqlRequest.setVariables(JsonUtils.parseMap(variables));
    }
    return pamirsPost(moduleName, gqlRequest, request, response);
}

variables.pathRequestHelper.preparePamirsRequestParam 中经 SessionPrepareApi.prepare 注入会话上下文,与前端 getSessionPath() 对应。

2.2.2 Excel 导出对照:ExcelFileServiceImpl#doExportSync

Excel 同步导出使用 mutationcreateExportTask),因为会创建导出任务记录;图片查看无此副作用,故用 query

同步导出的关键:劫持 FileClient.upload,将字节直接写入当前 HTTP 响应:

// pamirs-core/pamirs-file2/.../ExcelFileServiceImpl.java
public void doExportSync(ExcelExportTask exportTask, ExcelDefinitionContext context) {
    FileClient fileClient = new FileClient() {
        @Override
        public CdnFile upload(String fileName, byte[] data) {
            HttpServletResponse response = ...; // 从 RequestContextHolder 获取
            response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(data.length));
            response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(fileName, UTF_8));
            ServletOutputStream sos = response.getOutputStream();
            sos.write(data);
            sos.flush();
            return internalFileClient.upload(fileName, data);
        }
    };
    doExport0(exportTask, context, fileClient);
}

核心模式:GQL Function 执行业务逻辑 → 生成 byte[] → 通过 HttpServletResponse.getOutputStream() 直接输出,不再返回 JSON

2.3 mutation vs query 选型

场景 GQL 类型 后端注解 原因
图片查看 / 下载 query @Function(type = QUERY) 只读,按 ID 渲染并输出流
Excel 同步导出 mutation @Action 创建导出任务记录,附带写流
CDN 文件下载 query @Function(type = QUERY) 只读,代理已有文件流

2.4 端到端时序

GQL GET 请求生成图片文件流 — 技术实现方案


3. 通用图片流方案

3.1 调用方式

图片生成通过 GQL query 发起,一次 GET 请求完成渲染并输出文件流。前端通过同一 URL,配合后端 Content-Disposition 区分两种用途:

前端用法 后端响应头 效果
<img :src="imageUrl" /> Content-Disposition: inline 页面内预览
window.open(imageUrl, '_blank') Content-Disposition: attachment 新标签触发下载

请求参数中增加 download 字段控制响应头(见 §4、§5)。

3.2 模块选择

moduleName 说明
file 走 file 子系统,与 Excel 导出同模块
业务模块(如 order Action 挂在业务模型上,适合强业务耦合的图片生成

4. 后端实现指南

4.1 数据模型

@Model.model("order.BusinessImageRequest")
@Model(displayName = "业务图片请求")
public class BusinessImageRequest extends IdModel {

    @Field.String
    @Field(displayName = "业务ID")
    private String businessId;

    @Field.String
    @Field(displayName = "图片类型", summary = "如 BARCODE / LABEL / CERTIFICATE")
    private String imageType;

    @Field.Boolean
    @Field(displayName = "下载模式", summary = "true=attachment 下载,false=inline 预览")
    private Boolean download;

    @Field.Integer
    @Field(displayName = "宽度")
    private Integer width;

    @Field.Integer
    @Field(displayName = "高度")
    private Integer height;
}

4.2 Function 实现(查询型)

@Component
@Model.model(BusinessImageRequest.MODEL_MODEL)
public class BusinessImageAction {

    @Autowired
    private BusinessImageService businessImageService;

    @Function.Advanced(displayName = "渲染业务图片", type = FunctionTypeEnum.QUERY)
    @Function(openLevel = {FunctionOpenEnum.API, FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE})
    public BusinessImageRequest renderBusinessImage(BusinessImageRequest businessImageRequest) {
        businessImageService.renderImage(businessImageRequest);
        return businessImageRequest;
    }
}

4.3 Service:写图片流

@Service
public class BusinessImageServiceImpl implements BusinessImageService {

    public void renderImage(BusinessImageRequest request) {
        byte[] imageBytes = generateImage(request);

        HttpServletResponse response = Optional.ofNullable(RequestContextHolder.getRequestAttributes())
            .map(a -> (ServletRequestAttributes) a)
            .map(ServletRequestAttributes::getResponse)
            .orElseThrow(() -> new RuntimeException("未获取到 Http 响应"));

        try {
            String fileName = buildFileName(request);
            boolean download = Boolean.TRUE.equals(request.getDownload());
            String disposition = download ? "attachment" : "inline";

            response.setContentType("image/png");
            response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(imageBytes.length));
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                disposition + ";filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));

            ServletOutputStream out = response.getOutputStream();
            out.write(imageBytes);
            out.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private byte[] generateImage(BusinessImageRequest request) {
        // 1. 按 businessId 查业务数据
        // 2. 调用渲染引擎生成图片
        // 3. 返回 PNG/JPEG byte[]
        return ...;
    }
}

实现模式与 FileAction#downloadFormData 一致:在 GQL Query Function 调用链内直接写 HttpServletResponse,无需额外 Controller。


5. 前端实现指南

5.1 构造图片 URL(公共方法)

import { GQL, getSessionPath } from '@oinone/kunlun-request';
import { UrlHelper } from '@oinone/kunlun-shared';

interface BuildImageUrlOptions {
  moduleName: string;      // 如 'order'
  queryModel: string;      // 如 'businessImageRequest'(GQL 自动拼接为 businessImageRequestQuery)
  businessId: string;
  imageType: string;
  download?: boolean;      // false=预览(inline),true=下载(attachment)
}

async function buildImageUrl(options: BuildImageUrlOptions): Promise<string> {
  const { moduleName, queryModel, businessId, imageType, download = false } = options;

  const gql = await GQL.query(queryModel, 'renderBusinessImage')
    .buildRequest((builder) => {
      builder.buildObjectParameter('businessImageRequest', (b) => {
        b.stringParameter('businessId', businessId);
        b.stringParameter('imageType', imageType);
        b.booleanParameter('download', download);
      });
    })
    .buildResponse((b) => b.parameter('id'))
    .toString();

  const variables = { path: getSessionPath() };
  return UrlHelper.appendBasePath(
    `/pamirs/${moduleName}?query=${encodeURIComponent(gql)}&variables=${encodeURIComponent(
      JSON.stringify(variables)
    )}`
  );
}

5.2 方式一:<img src> 页面预览

<template>
  <img :src="imageUrl" alt="业务图片预览" />
</template>

<script setup lang="ts">
import { ref, watch } from 'vue';

const props = defineProps<{ businessId: string; imageType: string }>();
const imageUrl = ref('');

watch(
  () => [props.businessId, props.imageType],
  async () => {
    imageUrl.value = await buildImageUrl({
      moduleName: 'order',
      queryModel: 'businessImageRequest',
      businessId: props.businessId,
      imageType: props.imageType,
      download: false   // inline,供 img 内联渲染
    });
  },
  { immediate: true }
);
</script>

5.3 方式二:window.open 下载图片

async function downloadBusinessImage(businessId: string, imageType: string) {
  const url = await buildImageUrl({
    moduleName: 'order',
    queryModel: 'businessImageRequest',
    businessId,
    imageType,
    download: true    // attachment,触发浏览器下载
  });
  window.open(url, '_blank');
}

6. GQL 请求示例

6.1 预览(download: false

query {
  businessImageRequestQuery {
    renderBusinessImage(businessImageRequest: {
      businessId: "ORD-20260702-001"
      imageType: "BARCODE"
      download: false
    }) {
      id
    }
  }
}

6.2 下载(download: true

query {
  businessImageRequestQuery {
    renderBusinessImage(businessImageRequest: {
      businessId: "ORD-20260702-001"
      imageType: "BARCODE"
      download: true
    }) {
      id
    }
  }
}

6.3 完整 GET URL(解码后示意)

/{basePath}/pamirs/order?query=query{businessImageRequestQuery{renderBusinessImage(businessImageRequest:{businessId:"ORD-20260702-001",imageType:"BARCODE",download:false}){id}}}&variables={"path":"order.OrderList#search"}

7. 注意事项

7.1 URL 长度

浏览器与网关对 GET URL 长度有限制(通常 2KB~8KB)。图片请求参数应保持精简(businessIdimageType 等标量字段),复杂渲染逻辑在后端按 ID 自行查数。

7.2 响应类型

场景 download Content-Type Content-Disposition
<img src> 预览 false image/png inline;filename=...
window.open 下载 true image/png attachment;filename=...

7.3 鉴权

GET 请求依赖同源 Cookievariables.path 用于恢复视图会话;若 Function 不依赖会话上下文,可按需简化。

7.4 本地开发代理

本地开发时 /pamirs 需代理到后端,例如在 Vue CLI vue.config.js 中:

proxy: {
  '/pamirs': { target: 'http://your-backend-host:port', changeOrigin: true }
}

8. 实施检查清单

  • [ ] 后端:BusinessImageRequest 模型 + renderBusinessImage Query Function(写 HttpServletResponse
  • [ ] 后端:download 字段控制 inline / attachment
  • [ ] 前端:公共 buildImageUrl 方法(GQL.query 构造 + encodeURIComponent
  • [ ] 前端:variables.path = getSessionPath()
  • [ ] 前端:UrlHelper.appendBasePath 拼接 BASE_PATH
  • [ ] 联调:dev 代理 /pamirs 到后端
  • [ ] 验证:<img> 内联预览、window.open 下载、未登录拦截

9. 源码索引

9.1 前端(Oinone 框架)

说明 路径
查询型文件下载 URL @oinone/kunlun-vue-ui-common/.../UploadService.ts (generatorDownloadUrl)
GQL Builder @oinone/kunlun-request/.../gql.ts
URL 工具 @oinone/kunlun-shared/.../UrlHelper.ts

9.2 后端(oinone-pamirs)

说明 路径
GET/POST 网关 pamirs-framework/pamirs-gateways-graph-java/.../RequestController.java
会话准备 pamirs-framework/pamirs-gateways-graph-java/.../RequestHelper.java
查询型文件流(首选参考) pamirs-core/pamirs-file2/pamirs-file2-api/.../FileAction.java (downloadFormData)
Excel 同步写流(机制参考) pamirs-core/pamirs-file2/pamirs-file2-api/.../ExcelFileServiceImpl.java (doExportSync)
GET URL 编码测试 pamirs-core/pamirs-file2/pamirs-file2-core/.../RequestGetUrlTest.java

10. 相关文档

Oinone社区 作者:nation原创文章,如若转载,请注明出处:https://doc.oinone.top/kai-fa-shi-jian/25587.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

Like (0)
nation's avatarnation数式员工
Previous 2026年3月31日 pm11:21
Next 2026年7月27日

相关推荐

  • 问题排查调试工具使用手册

    当前端发起对应用的访问时,如果出现错误,那么我们可以通过以下方式进行简易排查,如果排查不出来,则也可以把排查工具给出的信息发送给Oinone官方售后进行进一步分析。本文将通过模拟异常信息,来介绍排查工具,提供了哪些辅助信息帮我们来快速定位问题。 排查工具基础介绍 通过前端页面的 /debug 路由路径访问调试工具的页面,假设我们的前端页面访问地址为http://localhost:6800,那么我们的排查工具请求路径就是 http://localhost:6800/debug排查工具可以帮我们排查前端页面元数据异常和后端接口的异常 排查前端页面元数据 将问题页面浏览器地址栏内 page 后的部分复制到调试工具的 debug 路由后重新发起请求,如图可以看到调试工具展示的信息,可以根据这些信息排查问题。 排查后端接口 后端接口出现问题后,打开(在原页面)浏览器的调试工具,切换到“网络”的标签页,在左侧的历史请求列表中找到需要调试的请求,右键会弹出菜单,点击菜单中的 “复制”,再次展开该菜单,点击二级菜单中的“以 fetch 格式复制”,这样可以复制到调试所需要的信息 2.复制调试信息到“接口调试”标签页内的文本框内,点击“发起请求”按钮获取调试结果 我们可以看到页面展示了该接口的各种调试信息,我们可以据此排查问题。 场景化的排查思路 业务代码中存在代码bug 报错后发起调试请求,我们可以看到,调试工具直接给出了异常抛出的具体代码所在位置,此时再切换到“全部堆栈”下,可以看到是业务类的233行导致的空指针异常,查看代码后分析可得是data.getName().eqauls方法在调用前未做条件判断补全该判断后代码可以正常执行 业务代码中没有直接的错误,异常在平台代码中抛出 报错后发起调试请求可以看到异常不在业务代码内再切换到“全部堆栈”,可以看到具体异常信息,提示core_demo_item表出现了重复的主键,该表是DemoItem模型的我们还可以切换到“sql调试”的标签页,可以看到出错的具体sql语句经过分析可以得知是240行的data.create()�重复创建数据导致的。 三、排查工具无法定位怎么办 当我们通过排查工具还是没有定位到问题的时候,可以通过调试页面的“下载全部调试数据”和“下载调试数据”按钮将调试信息的数据发送给官方售后人员帮助我们定位排查问题。 点击页面最顶部的“下载全部调试数据”按钮,可以下载页面调试数据和接口调试数据点击“调试接口”标签页内的“下载调试数据”按钮,可以下载接口调试数据 四、排查工具细节

    2024年5月21日
    3.1K00
  • 【后端】项目开发后端知识要点地图

    大类 明细 文档链接 平台基础 如何开发Action,理解前后端协议 如何开发Action,理解前后端协议 CDN配置及文件操作相关 OSS(CDN)配置和文件系统的一些操作 MINIO无公网访问地址下OSS的配置 MINIO无公网访问地址下OSS的配置 分库分表与自定义分表规则 分库分表与自定义分表规则 Oinone引入搜索引擎(增强模型) Oinone引入搜索引擎(增强模型) 引入搜索/增强模型Channel)常见问题解决办法 引入搜索(增强模型Channel)常见问题解决办法 框架之MessageHub(信息提示) 框架之MessageHub(信息提示) DsHint和BatchSizeHint的使用 DsHint(指定数据源)和BatchSizeHint(查询批次数量) Oinone连接外部数据源方案 Oinone连接外部数据源方案 如何自定义SQL(Mapper)语句 如何自定义SQL(Mapper)语句 IWrapper、QueryWrapper和LambdaQueryWrapper使用 IWrapper、QueryWrapper和LambdaQueryWrapper使用 如何在代码中使用自增ID、手动方式获取CODE 如何在代码中使用自增ID、手动方式获取CODE 函数之触发与定时配置和示例 函数之触发与定时配置和示例 函数之异步执行 函数之异步执行 查询时自定义排序字段和排序规则 查询时自定义排序字段和排序规则 非存储字段搜索 非存储字段搜索,适应灵活的搜索场景 枚举/二进制枚举/多值枚举 如何使用位运算的数据字典 全局首页及应用首页配置方法(homepage) 全局首页及应用首页配置方法(homepage) 缓存连接由Jedis切换为Lettuce 缓存连接由Jedis切换为Lettuce GraphQL请求:后端接口实现逻辑解析 GraphQL请求:后端接口实现逻辑解析 Nacos支持 Nacos作为注册中心 Oinone项目引入Nacos作为注册中心 Nacos作为配置中心 Oinone项目引入Nacos作为配置中心 Nacos做为注册中心调用SpringCloud服务 Nacos做为注册中心调用SpringCloud服务 分布式相关 如何构建分布式项目 Oinone如何支持构建分布式项目 构建分布式项目一些要点(dubbo日志关闭等) Oinone构建分布式项目一些注意点 信创支持 后端部署使用达梦数据库 【达梦】后端使用达梦数据库 后端部署使用PostgreSQL数据库 【PostgreSQL】后端使用PostgreSQL数据库 后端部署使用OpenGauss数据库 【OpenGauss】后端使用OpenGauss数据库 后端部署使用MSSQL数据库(SQLServer) 【MSSQL】后端部署使用MSSQL数据库(SQLServer) 东方通Web和Tomcat部署Oinone项目 东方通Web和Tomcat部署Oinone项目 常见扩展 如何增加用户中心的菜单 如何增加用户中心的菜单 导入导出 如何批量导入 如何批量导入 如何支持多Excel多个Sheet导入功能 如何支持多Excel多个Sheet导入功能 如何自定义Excel导入功能 如何自定义Excel导入功能 如何自定义Excel导出功能 如何自定义Excel导出功能 如何自定义表达式 如何自定义表达式 登录扩展 对接外部SSO Oinone登录扩展:对接SSO(4.7.8及之后的版本) 自定义占位符 自定义RSQL占位符及在权限中使用 自定义RSQL占位符(placeholder)及在权限中使用 自定义数据权限拦截处理 自定义数据权限拦截处理 设计器公共 后端无代码设计器Jar包启动方法 后端无代码设计器Jar包启动方法 界面设计器 页面跳转时增加跳转参数 页面跳转时增加跳转参数 界面设计器的导入导出 界面设计器的导入导出 流程设计器 项目中工作流引入和流程触发 项目中工作流引入和流程触发 流程扩展自定义函数示例代码汇总 工作流-流程扩展自定义函数示例代码汇总 工作流-流程代办等页面自定义 工作流-流程代办等页面自定义 审核撤回/回退/拒绝钩子使用 工作流审核撤回/回退/拒绝钩子使用 流程设计器的导入导出 流程设计器的导入导出 如何添加工作流运行时依赖 如何添加工作流运行时依赖 数据可视化 项目中图表设计器引入 数据可视化-项目中数据可视化的实现引入 自定义图表模版 数据可视化中图表的低无一体 图表设计器数据获取示例 数据可视化-数据可视化数据获取示例 如何添加数据可视化运行时依赖 如何添加数据可视化运行时依赖 图表设计器的设计数据导入导出 图表设计器的设计数据导入导出

    2024年5月21日
    3.0K00
  • 函数之异步执行

    总体介绍 异步任务是非常常见的一种开发模式,它在分布式的开发模式中有很多应用场景如: 高并发场景中,我们一般采用把长流程切短,用异步方式去掉可以异步的非关键功能,缩小主流程响应时间,提升用户体验 异构系统的集成调用,通过异步任务完成解耦与自动重试 分布式系统最终一致性的可选方案 本文将介绍oinone是如何结合Spring+TbSchedule来完成异步任务 构建第一个异步任务 新建PetShopService和PetShopServiceImpl 1、 新建PetShopService定义updatePetShops方法 package pro.shushi.pamirs.demo.api.service; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import java.util.List; @Fun(PetShopService.FUN_NAMESPACE) public interface PetShopService { String FUN_NAMESPACE = "demo.PetShop.PetShopService"; @Function void updatePetShops(List<PetShop> petShops); } 2、PetShopServiceImpl实现PetShopService接口并在updatePetShops增加@XAsync注解 package pro.shushi.pamirs.demo.core.service; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.demo.api.service.PetShopService; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.trigger.annotation.XAsync; import java.util.List; @Fun(PetShopService.FUN_NAMESPACE) @Component public class PetShopServiceImpl implements PetShopService { @Override @Function @XAsync(displayName = "异步批量更新宠物商店",limitRetryNumber = 3,nextRetryTimeValue = 60) public void updatePetShops(List<PetShop> petShops) { new PetShop().updateBatch(petShops); } } a. displayName = "异步批量更新宠物商店",定义异步任务展示名称b. limitRetryNumber = 3,定义任务失败重试次数,,默认:-1不断重试c. nextRetryTimeValue = 60,定义任务失败重试的时间数,默认:3d. nextRetryTimeUnit,定义任务失败重试的时间单位,默认:TimeUnitEnum.SECONDe. delayTime,定义任务延迟执行的时间数,默认:0f. delayTimeUnit,定义任务延迟执行的时间单位,默认:TimeUnitEnum.SECOND 修改PetShopBatchUpdateAction调用异步任务 引入PetShopService 修改conform方法,调用petShopService.updatePetShops方法 package pro.shushi.pamirs.demo.core.action; @Model.model(PetShopBatchUpdate.MODEL_MODEL) @Component public class PetShopBatchUpdateAction { @Autowired private PetShopService petShopService; @Action(displayName = "确定",bindingType = ViewTypeEnum.FORM,contextType = ActionContextTypeEnum.SINGLE) public PetShopBatchUpdate conform(PetShopBatchUpdate data){ List<PetShop> shops = ArgUtils.convert(PetShopProxy.MODEL_MODEL, PetShop.MODEL_MODEL,proxyList); // 调用异步任务 petShopService.updatePetShops(shops); }); return data; } } 不同应用如何隔离执行单元 在schedule跟模块部署一起的时候,多模块独立boot的情况下,需要做必要的配置。如果schedule独立部署则没有必要,因为全部走远程,不存在类找不到的问题 通过配置pamirs.zookeeper.rootPath,确保两组机器都能覆盖所有任务分片,这样不会漏数据 通过pamirs.event.schedule.ownSign来隔离。确保两组机器只取各自产生的数据,这样不会重复执行数据 pamirs: zookeeper: zkConnectString: 127.0.0.1:2181 zkSessionTimeout: 60000 rootPath: /demo event: enabled: true schedule: enabled: true ownSign: demo rocket-mq: namesrv-addr: 127.0.0.1:9876

    2024年5月25日
    2.1K00
  • 模型字段之序列化方式

    本文核心是带大家全面了解oinone的序列方式,包括支持的序列化类型、注意点、如果新增客户化序列化方式以及字段默认值的反序列化。 字段序列化方式说明 序列化方式 说明 备注 JSON JSON序列化 主要用于模型相关类型字段的序列化,是@Field.serialize默认选项 DOT 点拼接集合元素 COMMA 逗号拼接集合元素 BIT 按位与,2次幂数求和 非@Field.serialize可选项列表,用于二进制枚举序列化不需要配置,由oinone自动推断 字段序列化方式举例 1、给模型PetItemDetail 增加两个字段:petItemDetails类型为List 和 tags类型为List,并设置为不同的序列化方式,petItemDetails为JSON(缺省就是JSON,可不配),tags为COMMA。2、同时设置 @Field.Advanced(columnDefinition = "varchar(1024)"),防止序列化后存储过长。 @Model.model(PetItem.MODEL_MODEL) @Model(displayName = "宠物商品",summary="宠物商品",labelFields = {"itemName"}) public class PetItem extends AbstractDemoCodeModel{ public static final String MODEL_MODEL="demo.PetItem"; @Field(displayName = "品种") @Field.many2one @Field.Relation(relationFields = {"typeId"},referenceFields = {"id"}) private PetType type; @Field(displayName = "品种类型",invisible = true) private Long typeId; @Field(displayName = "详情", serialize = Field.serialize.JSON, store = NullableBoolEnum.TRUE) @Field.Advanced(columnDefinition = "varchar(1024)") private List<PetItemDetail> petItemDetails; @Field(displayName = "商品标签",serialize = Field.serialize.COMMA,store = NullableBoolEnum.TRUE,multi = true) @Field.Advanced(columnDefinition = "varchar(1024)") private List<String> tags; } 字段序列化注意点 必须使用Field#store属性将字段存储设置为NullableBoolEnum.TRUE。 使用Field#serialize属性指定序列化方式,默认为JSON。 如把PetItemDetail设置为存储模型,须在PetItem的petItemDetails字段上使用Field.Relation#store属性将关联关系存储设置为false。不然会同时存储petItemDetails字段和对应的PetItemDetail表记录 注册自己的序列化器 注册自己的序列化器(实现pro.shushi.pamirs.meta.api.core.orm.serialize.Serializer接口), 如oinone的DOT的序列化方式,用type()方法返回值做匹配,serialize和deserialize分别对应序列化和反序列化方法。 package pro.shushi.pamirs.framework.compute.serialize; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.core.orm.serialize.Serializer; import pro.shushi.pamirs.meta.common.constants.CharacterConstants; import pro.shushi.pamirs.meta.enmu.SerializeEnum; import pro.shushi.pamirs.meta.util.TypeUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 点表达式序列生成处理器实现 * @author shushi@shushi.pro * @version 1.0.0 */ @SuppressWarnings("rawtypes") @Slf4j @Component public class DotSerializeProcessor implements Serializer<Object, String> { @Override public String serialize(String ltype, Object value) { if (null == value) { return null; } if (List.class.isAssignableFrom(value.getClass())) { return StringUtils.join((List) value, CharacterConstants.SEPARATOR_DOT); } else { return StringUtils.join(Collections.singletonList(value), CharacterConstants.SEPARATOR_DOT); } } @SuppressWarnings("unchecked") @Override public Object deserialize(String ltype, String ltypeT, String value,…

    2024年5月24日
    2.3K00
  • Oinone协同开发使用手册

    概述 Oinone平台为开发人员提供了本地环境 – 测试环境之间的协同开发模式,可以使得开发人员在本地环境中设计的模型、函数等元数据实时被测试环境使用并设计。开发人员开发完成对应页面和功能后,可以部署至测试环境直接进行测试。 本篇文章将详细介绍协同开发模式在实际开发中的应用及相关内容。 名词解释 本地环境: 开发人员的本地启动环境 测试环境: 在测试服务器上部署的业务测试环境,业务工程服务和设计器服务共用中间件 业务工程服务:在测试服务器上部署的业务工程 设计器服务: 在测试服务器上部署的设计器镜像 一套环境:以测试环境为例,业务工程服务和设计器服务共同组成一套环境 生产环境: 在生产服务器上部署的业务生产环境 环境准备 部署了一个可用的设计器服务,并能正常访问。(需参照下文启动设计器环境内容进行相应修改) 准备一个用于开发的java工程。 准备一个用于部署测试环境的服务器。 协同参数介绍 用于测试环境的参数 -PmetaProtected=${value} 启用元数据保护,只有配置相同启动参数的服务才允许对元数据进行更新。通常该命令用于设计器服务和业务工程服务,并且两个环境需使用相同的元数据保护标记(value)进行启动。本地环境不使用该命令,以防止本地环境在协同开发时意外修改测试环境元数据,导致元数据混乱。 用法 java -jar boot.jar -PmetaProtected=pamirs 用于本地环境的配置 使用命令配置ownSign(推荐) java -jar boot.jar –pamirs.distribution.session.ownSign=demo 使用yaml配置ownSign pamirs: distribution: session: allMetaRefresh: false # 启用元数据全量刷新(备用配置,如遇元数据错误或混乱,启用该配置可进行恢复,使用一次后关闭即可) ownSign: demo # 协同开发元数据隔离标记,用于区分不同开发人员的本地环境,其他环境不允许使用 启动设计器环境 docker-run启动 -e PROGRAM_ARGS=-PmetaProtected=pamirs docker-compose启动 services: backend: container_name: designer-backend image: harbor.oinone.top/oinone/designer-backend-v5.0 restart: always environment: # 指定spring.profiles.active ARG_ENV: dev # 指定-Plifecycle ARG_LIFECYCLE: INSTALL # jvm参数 JVM_OPTIONS: "" # 程序参数 PROGRAM_ARGS: "-PmetaProtected=pamirs" PS: java [JVM_OPTIONS?] -jar boot.jar [PROGRAM_ARGS?] 开发流程示例图 具体使用步骤详见协同开发支持

    2024年7月24日
    2.7K00

Leave a Reply

Please Login to Comment