Files
FrameTour-BE/src/main/java/com/ycwl/basic/puzzle/claude.md
Jerry Yan 8991d68673 docs(claude): 更新设备数量匹配策略的描述
- 修改模式1的匹配逻辑为实际机位数大于等于deviceCount
- 修改模式2的匹配逻辑为从指定列表过滤后数量大于等于deviceCount
- 保持配置顺序并只取前N个设备进行匹配
2025-11-20 17:27:31 +08:00

28 KiB

Puzzle 拼图模块技术文档

📋 模块概述

Puzzle拼图模块是一个基于模板和元素的动态图片生成系统,支持按照预定义的模板配置,将动态数据渲染成最终的图片输出。常用于订单凭证、门票、证书等场景的图片生成。

核心能力:

  • 模板化图片生成:通过模板+元素+动态数据生成定制化图片
  • 多层次元素渲染:支持图片和文字元素的分层叠加
  • 灵活的样式配置:支持位置、大小、透明度、旋转、圆角等属性
  • 动态数据注入:通过elementKey进行动态数据替换
  • 智能自动填充:基于规则引擎自动选择和填充素材数据
  • 生成记录追踪:完整记录每次生成的参数和结果

典型应用场景:

  • 订单凭证图片生成(用户头像+订单信息)
  • 电子门票生成(二维码+用户信息)
  • 电子证书生成(用户信息+证书模板)
  • 营销海报生成(动态用户数据)

🏗️ 架构设计

分层结构

puzzle/
├── controller/          # API接口层
│   ├── PuzzleGenerateController.java      # 拼图生成接口
│   ├── PuzzleTemplateController.java      # 模板管理接口
│   └── PuzzleFillRuleController.java      # 填充规则管理接口
├── service/            # 业务逻辑层
│   ├── IPuzzleGenerateService.java
│   ├── IPuzzleTemplateService.java
│   ├── IPuzzleFillRuleService.java
│   └── impl/
│       ├── PuzzleGenerateServiceImpl.java
│       ├── PuzzleTemplateServiceImpl.java
│       └── PuzzleFillRuleServiceImpl.java
├── mapper/             # 数据访问层
│   ├── PuzzleTemplateMapper.java
│   ├── PuzzleElementMapper.java
│   ├── PuzzleGenerationRecordMapper.java
│   ├── PuzzleFillRuleMapper.java
│   └── PuzzleFillRuleItemMapper.java
│   # 拼图引擎会复用基础域的 com.ycwl.basic.mapper.SourceMapper(不在 puzzle 包内)
├── entity/             # 实体类
│   ├── PuzzleTemplateEntity.java          # 模板实体
│   ├── PuzzleElementEntity.java           # 元素实体
│   ├── PuzzleGenerationRecordEntity.java  # 生成记录实体
│   ├── PuzzleFillRuleEntity.java          # 填充规则实体
│   └── PuzzleFillRuleItemEntity.java      # 填充规则明细实体
├── dto/                # 数据传输对象
│   ├── PuzzleGenerateRequest.java         # 生成请求
│   ├── PuzzleGenerateResponse.java        # 生成响应
│   ├── PuzzleTemplateDTO.java             # 模板DTO
│   ├── PuzzleElementDTO.java              # 元素DTO
│   ├── TemplateCreateRequest.java         # 模板创建请求
│   ├── ElementCreateRequest.java          # 元素创建请求
│   ├── PuzzleFillRuleSaveRequest.java     # 填充规则保存请求
│   ├── PuzzleFillRuleDTO.java             # 填充规则DTO
│   └── PuzzleFillRuleItemDTO.java         # 填充规则明细DTO
├── fill/               # 自动填充引擎
│   ├── PuzzleElementFillEngine.java       # 填充引擎(核心)
│   ├── condition/                         # 条件策略
│   │   ├── ConditionStrategy.java
│   │   ├── ConditionEvaluator.java
│   │   ├── ConditionContext.java
│   │   ├── AlwaysConditionStrategy.java
│   │   ├── DeviceCountConditionStrategy.java
│   │   ├── DeviceCountRangeConditionStrategy.java
│   │   └── DeviceIdMatchConditionStrategy.java
│   ├── datasource/                        # 数据源解析
│   │   ├── DataSourceResolver.java
│   │   ├── DeviceImageDataSourceStrategy.java
│   │   ├── FaceUrlDataSourceStrategy.java
│   │   └── StaticValueDataSourceStrategy.java
│   └── enums/                             # 枚举定义
│       ├── ConditionType.java
│       ├── DataSourceType.java
│       └── SortStrategy.java
└── util/               # 工具类
    └── PuzzleImageRenderer.java           # 图片渲染引擎(核心)

设计模式

  1. 服务层模式(Service Layer):业务逻辑封装在service层,controller只负责接口适配
  2. DTO模式:使用独立的DTO对象处理API输入输出,与Entity分离
  3. 策略模式:图片适配模式(CONTAIN、COVER、FILL等)、条件匹配策略、数据源解析策略、排序策略
  4. 建造者模式:通过模板+元素配置构建最终图片
  5. 责任链模式:自动填充规则按优先级顺序匹配执行

🔐 运行时安全与一致性保证

  • 景区隔离强校验:PuzzleGenerateServiceImpl 会根据模板的 scenicId 判断是否允许当前请求生成图片;模板绑定了景区时,请求必须传入相同的 scenicId,否则直接拒绝,避免跨租户串用模板。
  • 自动填充参数兜底:自动填充引擎 PuzzleElementFillEngine 仅在 faceId + scenicId 同时存在时触发,且规则没有明细项时会继续匹配下一条,防止空规则截断后续逻辑。
  • 元素类型白名单ElementConfigHelper 仅允许 ElementType 枚举中已经落地的 TEXT、IMAGE 类型入库,杜绝未实现类型绕过验证。
  • 图片下载防护ImageElement 只接受公网 http/https 地址,自动阻断内网、环回以及 file:// 资源,并设置请求超时,缓解 SSRF 与资源阻塞风险。

🔧 核心组件详解

1. PuzzleImageRenderer - 图片渲染引擎

职责:核心渲染引擎,负责将模板配置和元素数据渲染成最终图片

关键方法

  • render(PuzzleTemplateEntity, List<PuzzleElementEntity>, Map<String, String>):主渲染方法
    • 创建画布(根据模板宽高)
    • 绘制背景(纯色或图片背景)
    • 按z-index顺序绘制元素
    • 返回BufferedImage对象

渲染流程

  1. 创建画布:根据模板的canvasWidth和canvasHeight创建BufferedImage
  2. 绘制背景:
    • backgroundType=0:绘制纯色背景(backgroundColor)
    • backgroundType=1:加载并绘制背景图片(backgroundImage)
  3. 按z-index排序元素列表(升序,确保层级正确)
  4. 逐个绘制元素:
    • elementType=1(图片元素):
      • 获取动态数据(dynamicData.get(elementKey))或使用defaultImageUrl
      • 下载图片
      • 根据imageFitMode缩放图片(CONTAIN/COVER/FILL/SCALE_DOWN)
      • 应用borderRadius(圆角)
      • 应用opacity(透明度)
      • 应用rotation(旋转)
      • 绘制到画布指定位置(xPosition, yPosition, width, height)
    • elementType=2(文字元素):
      • 获取动态数据或使用defaultText
      • 设置字体(fontFamily, fontSize, fontWeight, fontStyle)
      • 设置颜色(fontColor)
      • 应用textAlign(对齐方式)
      • 应用lineHeight(行高)
      • 处理maxLines(最大行数截断)
      • 应用textDecoration(下划线/删除线)
      • 应用opacity和rotation
      • 绘制到画布

技术要点

  • 使用Java AWT进行图形绘制
  • 使用Hutool工具库处理图片下载和基础操作
  • 支持图片圆角(通过Ellipse2D.Float或RoundRectangle2D.Float实现clip)
  • 支持透明度(通过AlphaComposite实现)
  • 支持旋转(通过Graphics2D.rotate)

2. PuzzleGenerateServiceImpl - 拼图生成服务

职责:协调拼图生成的完整流程

核心方法

PuzzleGenerateResponse generate(PuzzleGenerateRequest request)

生成流程

  1. 参数校验
    • 校验templateCode是否提供
    • 检查dynamicData是否为空
  2. 加载模板
    • 根据templateCode查询模板(PuzzleTemplateMapper)
    • 检查模板是否存在且启用(status=1)
    • 检查多租户权限(scenicId匹配)
  3. 加载元素
    • 根据templateId查询所有元素(PuzzleElementMapper)
    • 按z-index升序排序
    • 过滤未删除的元素(deleted=0)
  4. 调用渲染引擎
    • 调用PuzzleImageRenderer.render()
    • 传入模板、元素列表、动态数据
  5. 上传图片
    • 将BufferedImage转换为字节流
    • 估算文件大小
    • 上传到对象存储(OSS)
    • 获取图片URL
  6. 创建生成记录
    • 保存到puzzle_generation_record表
    • 记录参数、结果、耗时等信息
  7. 返回响应
    • 返回图片URL、宽高、文件大小等信息

辅助方法

  • createRecord():创建生成记录
  • uploadImage():上传图片到OSS
  • estimateFileSize():估算文件大小

3. PuzzleTemplateServiceImpl - 模板管理服务

职责:管理拼图模板和元素的CRUD操作

模板管理方法

  • createTemplate(TemplateCreateRequest):创建模板
  • updateTemplate(Long, TemplateCreateRequest):更新模板
  • deleteTemplate(Long):逻辑删除模板(软删除)
  • getTemplateDetail(Long):查询模板详情(包含元素列表)
  • getTemplateByCode(String):根据code查询模板
  • listTemplates(Long, String, Integer):分页查询模板列表

元素管理方法

  • addElement(ElementCreateRequest):添加单个元素
  • batchAddElements(Long, List<ElementCreateRequest>):批量添加元素
  • updateElement(Long, ElementCreateRequest):更新元素
  • deleteElement(Long):逻辑删除元素
  • getElementDetail(Long):查询元素详情

业务逻辑要点

  • 删除模板时会级联删除关联的所有元素
  • 支持多租户隔离(根据scenicId)
  • 支持按category分类查询
  • 支持按status过滤启用/禁用的模板

4. PuzzleElementFillEngine - 自动填充引擎(新功能)

职责:基于规则引擎自动选择和填充拼图元素的数据源

核心概念

  • 通过配置化的规则(而非硬编码)决定每个元素使用哪些素材
  • 支持基于机位数量、机位ID、人脸特征等多维度条件匹配
  • 支持多种数据源(机位图片、用户头像、二维码等)
  • 支持灵活的排序策略(最新、评分、随机等)
  • 支持优先级和降级策略

核心方法

Map<String, String> execute(Long templateId, Long faceId, Long scenicId)

执行流程

  1. 加载规则列表

    • 查询指定模板的所有启用规则(PuzzleFillRuleMapper.listByTemplateId
    • priority降序排序(优先级高的先执行)
  2. 构建上下文

    • 查询faceId关联的机位数量(SourceMapper.countDistinctDevicesByFaceId
    • 查询faceId关联的机位ID列表(SourceMapper.getDeviceIdsByFaceId
    • 构建ConditionContext对象
  3. 规则匹配

    • 遍历规则列表,调用ConditionEvaluator.evaluate()评估每条规则
    • 匹配到第一条符合条件的规则后停止(责任链模式)
  4. 执行填充

    • 查询匹配规则的所有明细项(PuzzleFillRuleItemMapper.listByRuleId
    • itemOrder排序
    • 对每条明细调用DataSourceResolver.resolve()解析数据源
    • 返回Map<elementKey, dataValue>

条件策略(Strategy Pattern)

策略类型 类名 匹配逻辑 配置示例
总是匹配 AlwaysConditionStrategy 总是返回true,用作兜底规则 {}
机位数量匹配(模式1) DeviceCountConditionStrategy 实际机位数 ≥ deviceCount {"deviceCount": 4}
机位数量匹配(模式2) DeviceCountConditionStrategy 从指定列表中过滤并匹配数量 ≥ deviceCount,只取前N个,保持配置顺序 {"deviceCount": 2, "deviceIds": [200, 300, 400]}
机位ID匹配 DeviceIdMatchConditionStrategy 匹配指定的机位ID(支持ANY/ALL模式) {"deviceIds": [200, 300], "matchMode": "ALL"}

数据源类型

类型 说明 sourceFilter 配置
DEVICE_IMAGE 机位图片 {"deviceIndex": 0, "type": 2} - deviceIndex指定使用第几个机位,type指定图片类型
USER_AVATAR 用户头像 {}
QR_CODE 二维码 {"content": "{orderId}"} - 支持变量替换

排序策略

策略 说明
LATEST 最新优先(按创建时间降序)
EARLIEST 最早优先(按创建时间升序)
SCORE_DESC 评分降序(适用于有评分的素材)
SCORE_ASC 评分升序
RANDOM 随机选择

降级策略

  • 每条明细可配置fallbackValue
  • 当数据源无法获取到值时,使用降级默认值
  • 如果降级值也为空,则跳过该元素的填充

技术要点

  • 使用Spring的@Component自动注册策略
  • 使用Jackson解析JSON配置
  • 缓存机位数量和机位列表,单次执行仅查询一次
  • 详细日志记录规则匹配和填充过程

使用场景

  • 根据机位数量选择不同布局(4机位用4宫格,6机位用六宫格)
  • 优先使用高质量机位的图片(指定机位200、300)
  • 多机位组合场景(只有机位A和B同时存在时使用特定布局)

性能优化

  • 规则数量建议不超过10条/模板
  • 优先级高的规则应配置更精确的条件
  • 使用ALWAYS策略作为兜底,确保总有规则匹配

5. Controller接口层

PuzzleGenerateController

POST /puzzle/generate

功能:生成拼图图片

请求体PuzzleGenerateRequest

{
  "templateCode": "order_certificate",
  "userId": 123,
  "orderId": "ORDER20250117001",
  "businessType": "order",
  "scenicId": 1,
  "dynamicData": {
    "userAvatar": "https://example.com/avatar.jpg",
    "userName": "张三",
    "orderNumber": "ORDER20250117001",
    "qrCode": "https://example.com/qr.png"
  },
  "outputFormat": "PNG",
  "quality": 90
}

响应AjaxResult<PuzzleGenerateResponse>

{
  "code": 200,
  "msg": "生成成功",
  "data": {
    "imageUrl": "https://oss.example.com/puzzle/xxx.png",
    "width": 750,
    "height": 1334,
    "fileSize": 245678,
    "recordId": 12345
  }
}

PuzzleTemplateController

提供模板和元素的完整CRUD接口(详见Controller类)


📊 数据模型

1. puzzle_template - 拼图模板表

字段 类型 说明
id BIGINT 主键ID
name VARCHAR(100) 模板名称
code VARCHAR(50) 模板编码(唯一,用于API调用)
canvas_width INT 画布宽度(像素)
canvas_height INT 画布高度(像素)
background_type TINYINT 背景类型:0-纯色 1-图片
background_color VARCHAR(20) 背景颜色(hex格式,如#FFFFFF)
background_image VARCHAR(500) 背景图片URL
description TEXT 模板描述
category VARCHAR(50) 模板分类(order/ticket/certificate等)
status TINYINT 状态:0-禁用 1-启用
scenic_id BIGINT 景区ID(多租户隔离)
create_time DATETIME 创建时间
update_time DATETIME 更新时间
deleted TINYINT 删除标记:0-未删除 1-已删除
deleted_at DATETIME 删除时间

索引

  • UNIQUE KEY uk_code (code, deleted)
  • KEY idx_scenic_id (scenic_id)
  • KEY idx_category_status (category, status, deleted)

2. puzzle_element - 拼图元素表

字段 类型 说明
id BIGINT 主键ID
template_id BIGINT 模板ID(外键)
element_type TINYINT 元素类型:1-图片 2-文字
element_key VARCHAR(50) 元素标识(用于动态数据映射)
element_name VARCHAR(100) 元素名称(便于管理)

位置和布局属性

字段 类型 说明
x_position INT X坐标(相对画布左上角)
y_position INT Y坐标(相对画布左上角)
width INT 宽度(像素)
height INT 高度(像素)
z_index INT 层级(数值越大越靠上)
rotation INT 旋转角度(0-360度,顺时针)
opacity INT 不透明度(0-100)

图片元素专有属性

字段 类型 说明
default_image_url VARCHAR(500) 默认图片URL
image_fit_mode VARCHAR(20) 图片适配模式:CONTAIN/COVER/FILL/SCALE_DOWN
border_radius INT 圆角半径(像素)

文字元素专有属性

字段 类型 说明
default_text TEXT 默认文本内容
font_family VARCHAR(50) 字体名称
font_size INT 字号(像素)
font_color VARCHAR(20) 字体颜色(hex)
font_weight VARCHAR(20) 字重:NORMAL/BOLD
font_style VARCHAR(20) 字体样式:NORMAL/ITALIC
text_align VARCHAR(20) 对齐方式:LEFT/CENTER/RIGHT
line_height DECIMAL(3,2) 行高倍数(如1.5)
max_lines INT 最大行数(NULL表示不限制)
text_decoration VARCHAR(20) 文本装饰:NONE/UNDERLINE/LINE_THROUGH

索引

  • KEY idx_template_id (template_id, deleted)
  • KEY idx_element_key (element_key)

3. puzzle_generation_record - 拼图生成记录表

字段 类型 说明
id BIGINT 主键ID
template_id BIGINT 模板ID
template_code VARCHAR(50) 模板编码(冗余)
user_id BIGINT 用户ID
order_id VARCHAR(50) 关联订单号
business_type VARCHAR(50) 业务类型
generation_params TEXT 生成参数(JSON格式)
result_image_url VARCHAR(500) 生成的图片URL
result_file_size BIGINT 文件大小(字节)
result_width INT 图片宽度
result_height INT 图片高度
status TINYINT 状态:0-生成中 1-成功 2-失败
error_message TEXT 错误信息(失败时)
generation_duration INT 生成耗时(毫秒)
retry_count INT 重试次数
scenic_id BIGINT 景区ID
client_ip VARCHAR(50) 客户端IP
user_agent VARCHAR(500) 客户端User-Agent
create_time DATETIME 创建时间
update_time DATETIME 更新时间

索引

  • KEY idx_user_id (user_id)
  • KEY idx_order_id (order_id)
  • KEY idx_template_id (template_id)
  • KEY idx_create_time (create_time)

4. puzzle_fill_rule - 拼图填充规则表

字段 类型 说明
id BIGINT 主键ID
template_id BIGINT 关联的模板ID(外键)
rule_name VARCHAR(100) 规则名称
condition_type VARCHAR(50) 条件类型:DEVICE_COUNT/DEVICE_COUNT_RANGE/DEVICE_ID_MATCH/ALWAYS
condition_value TEXT 条件配置(JSON格式)
priority INT 优先级(数值越大越优先)
enabled TINYINT 是否启用:0-禁用 1-启用
scenic_id BIGINT 景区ID(多租户隔离)
description TEXT 规则描述
create_time DATETIME 创建时间
update_time DATETIME 更新时间
deleted TINYINT 删除标记:0-未删除 1-已删除
deleted_at DATETIME 删除时间

索引

  • KEY idx_template_scenic (template_id, scenic_id, deleted)
  • KEY idx_priority (priority)

业务逻辑

  • 规则按priority降序排列执行
  • 匹配到第一条符合条件的规则后停止
  • 建议使用ALWAYS类型作为兜底规则(最低优先级)

5. puzzle_fill_rule_item - 拼图填充规则明细表

字段 类型 说明
id BIGINT 主键ID
rule_id BIGINT 关联的规则ID(外键)
element_key VARCHAR(50) 目标元素标识(对应puzzle_element的element_key)
data_source VARCHAR(50) 数据源类型:DEVICE_IMAGE/USER_AVATAR/QR_CODE等
source_filter TEXT 数据源过滤条件(JSON格式)
sort_strategy VARCHAR(50) 排序策略:LATEST/EARLIEST/SCORE_DESC/SCORE_ASC/RANDOM
fallback_value VARCHAR(500) 降级默认值(数据源无法获取时使用)
item_order INT 明细排序(决定执行顺序)
create_time DATETIME 创建时间
update_time DATETIME 更新时间
deleted TINYINT 删除标记:0-未删除 1-已删除
deleted_at DATETIME 删除时间

索引

  • KEY idx_rule_id (rule_id, deleted)
  • KEY idx_element_key (element_key)

业务逻辑

  • 明细项按item_order升序执行
  • 每条明细对应一个元素的填充逻辑
  • 支持降级策略(fallbackValue)

🔄 关键业务流程

拼图生成完整流程(含自动填充)

用户请求 → Controller接收
    ↓
验证templateCode和dynamicData
    ↓
根据templateCode查询模板(含权限校验)
    ↓
根据templateId查询所有元素(按z-index排序)
    ↓
【新增】调用PuzzleElementFillEngine.execute()(自动填充)
    ├─ 查询该模板的所有填充规则(按优先级排序)
    ├─ 构建ConditionContext(机位数量、机位列表等)
    ├─ 遍历规则进行条件匹配
    ├─ 找到匹配规则后,加载其明细列表
    ├─ 对每条明细调用DataSourceResolver解析数据源
    └─ 返回Map<elementKey, dataValue>
    ↓
合并自动填充数据和用户手动数据(用户数据优先级更高)
    ↓
调用PuzzleImageRenderer.render()
    ├─ 创建画布
    ├─ 绘制背景
    ├─ 遍历元素列表
    │   ├─ 图片元素:下载图片 → 缩放/圆角/透明度/旋转 → 绘制
    │   └─ 文字元素:设置字体样式 → 计算布局 → 绘制
    └─ 返回BufferedImage
    ↓
将BufferedImage转换为字节流
    ↓
上传到OSS获取URL
    ↓
创建生成记录(保存参数和结果)
    ↓
返回响应(imageUrl、width、height、fileSize等)

图片适配模式说明

CONTAIN(等比缩放适应)

  • 图片完全显示在区域内
  • 保持图片宽高比
  • 可能留白

COVER(等比缩放填充)

  • 完全覆盖目标区域
  • 保持图片宽高比
  • 可能裁剪图片

FILL(拉伸填充)

  • 完全填充目标区域
  • 不保持宽高比
  • 可能变形

SCALE_DOWN(缩小适应)

  • 类似CONTAIN
  • 但不放大图片(仅缩小)

📦 对外依赖

puzzle模块与其他模块的依赖关系:

依赖模块 依赖项 用途
storage OSS上传服务 上传生成的图片到对象存储
config 全局配置 获取系统配置信息
exception 自定义异常 业务异常处理
utils 工具类 通用工具方法

被依赖情况

  • order模块:订单凭证图片生成
  • ticket模块:电子门票图片生成
  • 其他需要动态图片生成的业务模块

🚀 扩展指南

1. 新增元素类型

如需支持新的元素类型(如二维码元素、形状元素等):

  1. PuzzleElementEntity中新增elementType枚举值
  2. PuzzleImageRenderer.render()中添加新类型的渲染逻辑
  3. 新增元素专有属性到puzzle_element表和实体类
  4. 更新DTO和请求对象

2. 新增图片格式支持

当前支持PNG和JPEG,如需支持WebP、SVG等:

  1. 更新PuzzleGenerateRequest.outputFormat校验逻辑
  2. 修改PuzzleGenerateServiceImpl.uploadImage()中的格式转换逻辑
  3. 注意浏览器兼容性

3. 新增渲染效果

如需支持阴影、边框、渐变等效果:

  1. PuzzleElementEntity中新增对应的属性字段
  2. PuzzleImageRenderer中实现对应的绘制逻辑
  3. 使用Java AWT的相关API(如setShadowdrawRect等)

4. 批量生成优化

如需支持批量生成(如批量生成门票):

  1. 新增批量生成接口POST /puzzle/batchGenerate
  2. 使用线程池并发处理
  3. 返回任务ID,支持异步查询结果

⚠️ 重要注意事项

1. 性能优化

  • 图片缓存:对于默认图片URL,考虑使用本地缓存避免重复下载
  • 并发控制:高并发场景下,生成接口应加限流保护
  • 资源释放:及时释放BufferedImage和Graphics2D对象,避免内存泄漏
  • 异步处理:对于复杂模板,考虑异步生成+回调通知

2. 安全性

  • URL校验:对dynamicData中的图片URL进行白名单校验,防止SSRF攻击
  • 文件大小限制:限制下载图片的大小,防止资源耗尽
  • 权限控制:确保scenicId隔离,防止越权访问
  • 输入校验:严格校验所有输入参数,防止XSS和注入攻击

3. 多租户隔离

  • 所有查询必须带上scenicId条件
  • 创建模板和元素时必须关联正确的scenicId
  • 生成拼图时校验模板的scenicId权限

4. 错误处理

  • 图片下载失败时的降级策略(使用默认图片)
  • 渲染失败时记录详细错误日志
  • 对外暴露友好的错误提示

5. 字体问题

  • 字体文件:确保服务器安装了模板使用的字体文件
  • 中文字体:Linux服务器需要安装中文字体(如文泉驿)
  • 字体回退:设置字体回退机制,避免乱码

6. 数据一致性

  • 删除模板时级联删除元素(软删除)
  • 更新模板状态时考虑对正在生成的任务的影响
  • 生成记录不可删除,仅供审计和统计

7. 监控和日志

  • 记录每次生成的耗时,监控性能
  • 记录生成失败的详细原因,便于排查
  • 统计各模板的使用频率,优化热点模板

📈 性能指标参考

典型性能数据(测试环境):

  • 单张简单拼图(2-3个元素):< 500ms
  • 单张复杂拼图(10+个元素):< 1500ms
  • 图片下载耗时:200-500ms(取决于网络)
  • 渲染耗时:50-200ms
  • OSS上传耗时:100-300ms

优化建议

  • 使用CDN加速图片下载
  • 预热常用模板的背景图片
  • 使用Redis缓存模板和元素配置

📝 示例代码

创建模板示例

// 1. 创建模板
TemplateCreateRequest templateReq = new TemplateCreateRequest();
templateReq.setName("订单凭证模板");
templateReq.setCode("order_certificate_v1");
templateReq.setCanvasWidth(750);
templateReq.setCanvasHeight(1334);
templateReq.setBackgroundType(1);
templateReq.setBackgroundImage("https://oss.example.com/bg.jpg");
templateReq.setCategory("order");
templateReq.setScenicId(1L);

Long templateId = templateService.createTemplate(templateReq);

// 2. 添加元素 - 用户头像(图片元素)
ElementCreateRequest avatarElement = new ElementCreateRequest();
avatarElement.setTemplateId(templateId);
avatarElement.setElementType(1); // 图片
avatarElement.setElementKey("userAvatar");
avatarElement.setElementName("用户头像");
avatarElement.setXPosition(50);
avatarElement.setYPosition(100);
avatarElement.setWidth(100);
avatarElement.setHeight(100);
avatarElement.setZIndex(10);
avatarElement.setDefaultImageUrl("https://oss.example.com/default-avatar.png");
avatarElement.setImageFitMode("COVER");
avatarElement.setBorderRadius(50); // 圆形头像
avatarElement.setOpacity(100);

templateService.addElement(avatarElement);

// 3. 添加元素 - 用户名(文字元素)
ElementCreateRequest nameElement = new ElementCreateRequest();
nameElement.setTemplateId(templateId);
nameElement.setElementType(2); // 文字
nameElement.setElementKey("userName");
nameElement.setElementName("用户名");
nameElement.setXPosition(170);
nameElement.setYPosition(120);
nameElement.setWidth(300);
nameElement.setHeight(60);
nameElement.setZIndex(20);
nameElement.setDefaultText("用户名");
nameElement.setFontFamily("PingFang SC");
nameElement.setFontSize(28);
nameElement.setFontColor("#333333");
nameElement.setFontWeight("BOLD");
nameElement.setTextAlign("LEFT");
nameElement.setLineHeight(new BigDecimal("1.5"));

templateService.addElement(nameElement);

生成拼图示例

// 调用生成接口
PuzzleGenerateRequest request = new PuzzleGenerateRequest();
request.setTemplateCode("order_certificate_v1");
request.setUserId(123L);
request.setOrderId("ORDER20250117001");
request.setBusinessType("order");
request.setScenicId(1L);

Map<String, String> dynamicData = new HashMap<>();
dynamicData.put("userAvatar", "https://oss.example.com/user123/avatar.jpg");
dynamicData.put("userName", "张三");
dynamicData.put("orderNumber", "ORDER20250117001");
dynamicData.put("qrCode", "https://oss.example.com/qr/ORDER20250117001.png");
request.setDynamicData(dynamicData);

request.setOutputFormat("PNG");
request.setQuality(90);

PuzzleGenerateResponse response = generateService.generate(request);
System.out.println("生成成功,图片URL: " + response.getImageUrl());