You've already forked FrameTour-BE
feat(puzzle): 实现拼图生成功能模块
- 新增拼图生成控制器 PuzzleGenerateController,支持 /api/puzzle/generate 接口 - 新增拼图模板管理控制器 PuzzleTemplateController,提供完整的 CRUD 和元素管理功能 - 定义拼图相关 DTO 类,包括模板、元素、生成请求与响应等数据传输对象 - 创建拼图相关的实体类 PuzzleTemplateEntity、PuzzleElementEntity 和 PuzzleGenerationRecordEntity - 实现 Mapper 接口用于数据库操作,支持模板和元素的增删改查及生成记录管理 - 开发 PuzzleGenerateServiceImpl 服务,完成从模板渲染到图片上传的完整流程 - 提供 PuzzleTemplateServiceImpl 服务,实现模板及其元素的全生命周期管理 - 引入 PuzzleImageRenderer 工具类负责图像合成渲染逻辑 - 支持将生成结果上传至 OSS 并记录生成过程的日志和元数据
This commit is contained in:
153
src/main/resources/mapper/PuzzleElementMapper.xml
Normal file
153
src/main/resources/mapper/PuzzleElementMapper.xml
Normal file
@@ -0,0 +1,153 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ycwl.basic.puzzle.mapper.PuzzleElementMapper">
|
||||
|
||||
<!-- 结果映射 -->
|
||||
<resultMap id="BaseResultMap" type="com.ycwl.basic.puzzle.entity.PuzzleElementEntity">
|
||||
<id column="id" property="id"/>
|
||||
<result column="template_id" property="templateId"/>
|
||||
<result column="element_type" property="elementType"/>
|
||||
<result column="element_key" property="elementKey"/>
|
||||
<result column="element_name" property="elementName"/>
|
||||
<result column="x_position" property="xPosition"/>
|
||||
<result column="y_position" property="yPosition"/>
|
||||
<result column="width" property="width"/>
|
||||
<result column="height" property="height"/>
|
||||
<result column="z_index" property="zIndex"/>
|
||||
<result column="rotation" property="rotation"/>
|
||||
<result column="opacity" property="opacity"/>
|
||||
<result column="default_image_url" property="defaultImageUrl"/>
|
||||
<result column="image_fit_mode" property="imageFitMode"/>
|
||||
<result column="border_radius" property="borderRadius"/>
|
||||
<result column="default_text" property="defaultText"/>
|
||||
<result column="font_family" property="fontFamily"/>
|
||||
<result column="font_size" property="fontSize"/>
|
||||
<result column="font_color" property="fontColor"/>
|
||||
<result column="font_weight" property="fontWeight"/>
|
||||
<result column="font_style" property="fontStyle"/>
|
||||
<result column="text_align" property="textAlign"/>
|
||||
<result column="line_height" property="lineHeight"/>
|
||||
<result column="max_lines" property="maxLines"/>
|
||||
<result column="text_decoration" property="textDecoration"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
<result column="deleted_at" property="deletedAt"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 基础列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, template_id, element_type, element_key, element_name,
|
||||
x_position, y_position, width, height, z_index, rotation, opacity,
|
||||
default_image_url, image_fit_mode, border_radius,
|
||||
default_text, font_family, font_size, font_color, font_weight, font_style,
|
||||
text_align, line_height, max_lines, text_decoration,
|
||||
create_time, update_time, deleted, deleted_at
|
||||
</sql>
|
||||
|
||||
<!-- 根据ID查询 -->
|
||||
<select id="getById" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM puzzle_element
|
||||
WHERE id = #{id} AND deleted = 0
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 根据模板ID查询元素列表(按z-index排序) -->
|
||||
<select id="getByTemplateId" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM puzzle_element
|
||||
WHERE template_id = #{templateId} AND deleted = 0
|
||||
ORDER BY z_index ASC, id ASC
|
||||
</select>
|
||||
|
||||
<!-- 插入 -->
|
||||
<insert id="insert" parameterType="com.ycwl.basic.puzzle.entity.PuzzleElementEntity"
|
||||
useGeneratedKeys="true" keyProperty="id">
|
||||
INSERT INTO puzzle_element (
|
||||
template_id, element_type, element_key, element_name,
|
||||
x_position, y_position, width, height, z_index, rotation, opacity,
|
||||
default_image_url, image_fit_mode, border_radius,
|
||||
default_text, font_family, font_size, font_color, font_weight, font_style,
|
||||
text_align, line_height, max_lines, text_decoration,
|
||||
create_time, update_time, deleted
|
||||
) VALUES (
|
||||
#{templateId}, #{elementType}, #{elementKey}, #{elementName},
|
||||
#{xPosition}, #{yPosition}, #{width}, #{height}, #{zIndex}, #{rotation}, #{opacity},
|
||||
#{defaultImageUrl}, #{imageFitMode}, #{borderRadius},
|
||||
#{defaultText}, #{fontFamily}, #{fontSize}, #{fontColor}, #{fontWeight}, #{fontStyle},
|
||||
#{textAlign}, #{lineHeight}, #{maxLines}, #{textDecoration},
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 批量插入 -->
|
||||
<insert id="batchInsert">
|
||||
INSERT INTO puzzle_element (
|
||||
template_id, element_type, element_key, element_name,
|
||||
x_position, y_position, width, height, z_index, rotation, opacity,
|
||||
default_image_url, image_fit_mode, border_radius,
|
||||
default_text, font_family, font_size, font_color, font_weight, font_style,
|
||||
text_align, line_height, max_lines, text_decoration,
|
||||
create_time, update_time, deleted
|
||||
) VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(
|
||||
#{item.templateId}, #{item.elementType}, #{item.elementKey}, #{item.elementName},
|
||||
#{item.xPosition}, #{item.yPosition}, #{item.width}, #{item.height}, #{item.zIndex}, #{item.rotation}, #{item.opacity},
|
||||
#{item.defaultImageUrl}, #{item.imageFitMode}, #{item.borderRadius},
|
||||
#{item.defaultText}, #{item.fontFamily}, #{item.fontSize}, #{item.fontColor}, #{item.fontWeight}, #{item.fontStyle},
|
||||
#{item.textAlign}, #{item.lineHeight}, #{item.maxLines}, #{item.textDecoration},
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 更新 -->
|
||||
<update id="update" parameterType="com.ycwl.basic.puzzle.entity.PuzzleElementEntity">
|
||||
UPDATE puzzle_element
|
||||
<set>
|
||||
<if test="elementType != null">element_type = #{elementType},</if>
|
||||
<if test="elementKey != null">element_key = #{elementKey},</if>
|
||||
<if test="elementName != null">element_name = #{elementName},</if>
|
||||
<if test="xPosition != null">x_position = #{xPosition},</if>
|
||||
<if test="yPosition != null">y_position = #{yPosition},</if>
|
||||
<if test="width != null">width = #{width},</if>
|
||||
<if test="height != null">height = #{height},</if>
|
||||
<if test="zIndex != null">z_index = #{zIndex},</if>
|
||||
<if test="rotation != null">rotation = #{rotation},</if>
|
||||
<if test="opacity != null">opacity = #{opacity},</if>
|
||||
<if test="defaultImageUrl != null">default_image_url = #{defaultImageUrl},</if>
|
||||
<if test="imageFitMode != null">image_fit_mode = #{imageFitMode},</if>
|
||||
<if test="borderRadius != null">border_radius = #{borderRadius},</if>
|
||||
<if test="defaultText != null">default_text = #{defaultText},</if>
|
||||
<if test="fontFamily != null">font_family = #{fontFamily},</if>
|
||||
<if test="fontSize != null">font_size = #{fontSize},</if>
|
||||
<if test="fontColor != null">font_color = #{fontColor},</if>
|
||||
<if test="fontWeight != null">font_weight = #{fontWeight},</if>
|
||||
<if test="fontStyle != null">font_style = #{fontStyle},</if>
|
||||
<if test="textAlign != null">text_align = #{textAlign},</if>
|
||||
<if test="lineHeight != null">line_height = #{lineHeight},</if>
|
||||
<if test="maxLines != null">max_lines = #{maxLines},</if>
|
||||
<if test="textDecoration != null">text_decoration = #{textDecoration},</if>
|
||||
update_time = NOW()
|
||||
</set>
|
||||
WHERE id = #{id} AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 逻辑删除 -->
|
||||
<update id="deleteById">
|
||||
UPDATE puzzle_element
|
||||
SET deleted = 1, deleted_at = NOW(), update_time = NOW()
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
<!-- 根据模板ID删除所有元素 -->
|
||||
<update id="deleteByTemplateId">
|
||||
UPDATE puzzle_element
|
||||
SET deleted = 1, deleted_at = NOW(), update_time = NOW()
|
||||
WHERE template_id = #{templateId}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
120
src/main/resources/mapper/PuzzleGenerationRecordMapper.xml
Normal file
120
src/main/resources/mapper/PuzzleGenerationRecordMapper.xml
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ycwl.basic.puzzle.mapper.PuzzleGenerationRecordMapper">
|
||||
|
||||
<!-- 结果映射 -->
|
||||
<resultMap id="BaseResultMap" type="com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity">
|
||||
<id column="id" property="id"/>
|
||||
<result column="template_id" property="templateId"/>
|
||||
<result column="template_code" property="templateCode"/>
|
||||
<result column="user_id" property="userId"/>
|
||||
<result column="order_id" property="orderId"/>
|
||||
<result column="business_type" property="businessType"/>
|
||||
<result column="generation_params" property="generationParams"/>
|
||||
<result column="result_image_url" property="resultImageUrl"/>
|
||||
<result column="result_file_size" property="resultFileSize"/>
|
||||
<result column="result_width" property="resultWidth"/>
|
||||
<result column="result_height" property="resultHeight"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="error_message" property="errorMessage"/>
|
||||
<result column="generation_duration" property="generationDuration"/>
|
||||
<result column="retry_count" property="retryCount"/>
|
||||
<result column="scenic_id" property="scenicId"/>
|
||||
<result column="client_ip" property="clientIp"/>
|
||||
<result column="user_agent" property="userAgent"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 基础列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, template_id, template_code, user_id, order_id, business_type,
|
||||
generation_params, result_image_url, result_file_size, result_width, result_height,
|
||||
status, error_message, generation_duration, retry_count,
|
||||
scenic_id, client_ip, user_agent, create_time, update_time
|
||||
</sql>
|
||||
|
||||
<!-- 根据ID查询 -->
|
||||
<select id="getById" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM puzzle_generation_record
|
||||
WHERE id = #{id}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 查询用户的生成记录列表 -->
|
||||
<select id="listByUserId" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM puzzle_generation_record
|
||||
WHERE user_id = #{userId}
|
||||
ORDER BY create_time DESC
|
||||
<if test="limit != null">
|
||||
LIMIT #{limit}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 查询订单的生成记录列表 -->
|
||||
<select id="listByOrderId" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM puzzle_generation_record
|
||||
WHERE order_id = #{orderId}
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 插入 -->
|
||||
<insert id="insert" parameterType="com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity"
|
||||
useGeneratedKeys="true" keyProperty="id">
|
||||
INSERT INTO puzzle_generation_record (
|
||||
template_id, template_code, user_id, order_id, business_type,
|
||||
generation_params, result_image_url, result_file_size, result_width, result_height,
|
||||
status, error_message, generation_duration, retry_count,
|
||||
scenic_id, client_ip, user_agent, create_time, update_time
|
||||
) VALUES (
|
||||
#{templateId}, #{templateCode}, #{userId}, #{orderId}, #{businessType},
|
||||
#{generationParams}, #{resultImageUrl}, #{resultFileSize}, #{resultWidth}, #{resultHeight},
|
||||
#{status}, #{errorMessage}, #{generationDuration}, #{retryCount},
|
||||
#{scenicId}, #{clientIp}, #{userAgent}, NOW(), NOW()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 更新 -->
|
||||
<update id="update" parameterType="com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity">
|
||||
UPDATE puzzle_generation_record
|
||||
<set>
|
||||
<if test="resultImageUrl != null">result_image_url = #{resultImageUrl},</if>
|
||||
<if test="resultFileSize != null">result_file_size = #{resultFileSize},</if>
|
||||
<if test="resultWidth != null">result_width = #{resultWidth},</if>
|
||||
<if test="resultHeight != null">result_height = #{resultHeight},</if>
|
||||
<if test="status != null">status = #{status},</if>
|
||||
<if test="errorMessage != null">error_message = #{errorMessage},</if>
|
||||
<if test="generationDuration != null">generation_duration = #{generationDuration},</if>
|
||||
<if test="retryCount != null">retry_count = #{retryCount},</if>
|
||||
update_time = NOW()
|
||||
</set>
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
<!-- 更新为成功状态 -->
|
||||
<update id="updateSuccess">
|
||||
UPDATE puzzle_generation_record
|
||||
SET status = 1,
|
||||
result_image_url = #{resultImageUrl},
|
||||
result_file_size = #{resultFileSize},
|
||||
result_width = #{resultWidth},
|
||||
result_height = #{resultHeight},
|
||||
generation_duration = #{generationDuration},
|
||||
update_time = NOW()
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
<!-- 更新为失败状态 -->
|
||||
<update id="updateFail">
|
||||
UPDATE puzzle_generation_record
|
||||
SET status = 2,
|
||||
error_message = #{errorMessage},
|
||||
update_time = NOW()
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
114
src/main/resources/mapper/PuzzleTemplateMapper.xml
Normal file
114
src/main/resources/mapper/PuzzleTemplateMapper.xml
Normal file
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ycwl.basic.puzzle.mapper.PuzzleTemplateMapper">
|
||||
|
||||
<!-- 结果映射 -->
|
||||
<resultMap id="BaseResultMap" type="com.ycwl.basic.puzzle.entity.PuzzleTemplateEntity">
|
||||
<id column="id" property="id"/>
|
||||
<result column="name" property="name"/>
|
||||
<result column="code" property="code"/>
|
||||
<result column="canvas_width" property="canvasWidth"/>
|
||||
<result column="canvas_height" property="canvasHeight"/>
|
||||
<result column="background_type" property="backgroundType"/>
|
||||
<result column="background_color" property="backgroundColor"/>
|
||||
<result column="background_image" property="backgroundImage"/>
|
||||
<result column="description" property="description"/>
|
||||
<result column="category" property="category"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="scenic_id" property="scenicId"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
<result column="deleted_at" property="deletedAt"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 基础列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, name, code, canvas_width, canvas_height, background_type, background_color,
|
||||
background_image, description, category, status, scenic_id, create_time, update_time, deleted, deleted_at
|
||||
</sql>
|
||||
|
||||
<!-- 根据ID查询 -->
|
||||
<select id="getById" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM puzzle_template
|
||||
WHERE id = #{id} AND deleted = 0
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 根据编码查询 -->
|
||||
<select id="getByCode" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM puzzle_template
|
||||
WHERE code = #{code} AND deleted = 0
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 查询列表 -->
|
||||
<select id="list" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
FROM puzzle_template
|
||||
WHERE deleted = 0
|
||||
<if test="scenicId != null">
|
||||
AND (scenic_id = #{scenicId} OR scenic_id IS NULL)
|
||||
</if>
|
||||
<if test="category != null and category != ''">
|
||||
AND category = #{category}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 插入 -->
|
||||
<insert id="insert" parameterType="com.ycwl.basic.puzzle.entity.PuzzleTemplateEntity"
|
||||
useGeneratedKeys="true" keyProperty="id">
|
||||
INSERT INTO puzzle_template (
|
||||
name, code, canvas_width, canvas_height, background_type, background_color,
|
||||
background_image, description, category, status, scenic_id, create_time, update_time, deleted
|
||||
) VALUES (
|
||||
#{name}, #{code}, #{canvasWidth}, #{canvasHeight}, #{backgroundType}, #{backgroundColor},
|
||||
#{backgroundImage}, #{description}, #{category}, #{status}, #{scenicId}, NOW(), NOW(), 0
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 更新 -->
|
||||
<update id="update" parameterType="com.ycwl.basic.puzzle.entity.PuzzleTemplateEntity">
|
||||
UPDATE puzzle_template
|
||||
<set>
|
||||
<if test="name != null">name = #{name},</if>
|
||||
<if test="code != null">code = #{code},</if>
|
||||
<if test="canvasWidth != null">canvas_width = #{canvasWidth},</if>
|
||||
<if test="canvasHeight != null">canvas_height = #{canvasHeight},</if>
|
||||
<if test="backgroundType != null">background_type = #{backgroundType},</if>
|
||||
<if test="backgroundColor != null">background_color = #{backgroundColor},</if>
|
||||
<if test="backgroundImage != null">background_image = #{backgroundImage},</if>
|
||||
<if test="description != null">description = #{description},</if>
|
||||
<if test="category != null">category = #{category},</if>
|
||||
<if test="status != null">status = #{status},</if>
|
||||
<if test="scenicId != null">scenic_id = #{scenicId},</if>
|
||||
update_time = NOW()
|
||||
</set>
|
||||
WHERE id = #{id} AND deleted = 0
|
||||
</update>
|
||||
|
||||
<!-- 逻辑删除 -->
|
||||
<update id="deleteById">
|
||||
UPDATE puzzle_template
|
||||
SET deleted = 1, deleted_at = NOW(), update_time = NOW()
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
<!-- 检查编码是否存在 -->
|
||||
<select id="countByCode" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM puzzle_template
|
||||
WHERE code = #{code} AND deleted = 0
|
||||
<if test="excludeId != null">
|
||||
AND id != #{excludeId}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user