Files
BMS/bms-back/bms-system/src/main/resources/mapper/receivable/ArBillGenerateTaskMapper.xml
T
2026-11-11 09:54:09 +08:00

187 lines
7.9 KiB
XML

<?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.bms.receivable.mapper.ArBillGenerateTaskMapper">
<!-- 生成任务表是异步任务的持久化队列;状态更新均带前置状态条件,保证多节点抢占幂等。 -->
<resultMap id="ArBillGenerateTaskResult" type="ArBillGenerateTask">
<id property="taskId" column="task_id"/>
<result property="taskNo" column="task_no"/>
<result property="arBillId" column="ar_bill_id"/>
<result property="selectionMode" column="selection_mode"/>
<result property="requestPayload" column="request_payload"/>
<result property="snapshotCreateTime" column="snapshot_create_time"/>
<result property="lastSourceBillId" column="last_source_bill_id"/>
<result property="totalCount" column="total_count"/>
<result property="processedCount" column="processed_count"/>
<result property="successCount" column="success_count"/>
<result property="totalAmount" column="total_amount"/>
<result property="status" column="status"/>
<result property="errorMessage" column="error_message"/>
<result property="requestToken" column="request_token"/>
<result property="createBy" column="create_by"/>
<result property="createUserId" column="create_user_id"/>
<result property="createTime" column="create_time"/>
<result property="startedAt" column="started_at"/>
<result property="heartbeatTime" column="heartbeat_time"/>
<result property="finishedAt" column="finished_at"/>
</resultMap>
<sql id="columns">
select task_id,
task_no,
ar_bill_id,
selection_mode,
request_payload,
snapshot_create_time,
last_source_bill_id,
total_count,
processed_count,
success_count,
total_amount,
status,
error_message,
request_token,
create_by,
create_user_id,
create_time,
started_at,
heartbeat_time,
finished_at
from ar_bill_generate_task
</sql>
<select id="selectById" parameterType="long" resultMap="ArBillGenerateTaskResult">
<include refid="columns"/>
where task_id = #{taskId}
</select>
<select id="selectByRequestToken" parameterType="string" resultMap="ArBillGenerateTaskResult">
<include refid="columns"/>
where request_token = #{requestToken}
</select>
<!-- 前端恢复页面时查询当前用户仍需展示的任务。 -->
<select id="selectActiveByCreateBy" resultMap="ArBillGenerateTaskResult">
<include refid="columns"/>
where create_by = #{createBy}
and status in ('WAITING', 'RUNNING', 'CANCEL_REQUESTED')
order by create_time desc, task_id desc
</select>
<select id="selectActiveByCreateByScoped" resultMap="ArBillGenerateTaskResult">
<include refid="columns"/>
where create_by = #{createBy}
and status in ('WAITING', 'RUNNING', 'CANCEL_REQUESTED')
AND EXISTS (SELECT 1 FROM ar_bill b WHERE b.ar_bill_id = ar_bill_generate_task.ar_bill_id
<include refid="com.bms.receivable.mapper.ArBillScopeMapper.billScope"/>)
order by create_time desc, task_id desc
</select>
<!-- 定时调度器按创建时间顺序取待处理任务。 -->
<select id="selectWaiting" resultMap="ArBillGenerateTaskResult">
<include refid="columns"/>
where status = 'WAITING'
order by create_time asc, task_id asc
limit #{limit}
</select>
<insert id="insert" parameterType="ArBillGenerateTask" useGeneratedKeys="true" keyProperty="taskId">
insert into ar_bill_generate_task (task_no, ar_bill_id, selection_mode, request_payload, snapshot_create_time,
last_source_bill_id, total_count, processed_count, success_count,
total_amount,
status, error_message, request_token, create_by, create_user_id, create_time, started_at,
heartbeat_time, finished_at)
values (#{taskNo}, #{arBillId}, #{selectionMode}, #{requestPayload}, #{snapshotCreateTime},
#{lastSourceBillId}, #{totalCount}, #{processedCount}, #{successCount}, #{totalAmount},
#{status}, #{errorMessage}, #{requestToken}, #{createBy}, #{createUserId}, sysdate(), #{startedAt},
#{heartbeatTime}, #{finishedAt})
</insert>
<!-- 条件更新抢占任务;返回 1 才表示当前节点真正拿到执行权。 -->
<update id="markRunning">
update ar_bill_generate_task
set status = 'RUNNING',
started_at = coalesce(started_at, #{startedAt}),
heartbeat_time = #{startedAt}
where task_id = #{taskId}
and status = 'WAITING'
</update>
<update id="updateTotalCount">
update ar_bill_generate_task
set total_count = #{totalCount},
heartbeat_time = sysdate()
where task_id = #{taskId}
and status = 'RUNNING'
</update>
<!-- 每批成功后写入游标、计数和心跳;游标用于故障恢复后继续扫描。 -->
<update id="updateProgress">
update ar_bill_generate_task
set last_source_bill_id = #{lastSourceBillId},
processed_count = #{processedCount},
success_count = #{successCount},
total_amount = #{totalAmount},
heartbeat_time = #{heartbeatTime}
where task_id = #{taskId}
and status = 'RUNNING'
</update>
<update id="markSuccess">
update ar_bill_generate_task
set status = 'SUCCESS',
finished_at = #{finishedAt},
heartbeat_time = #{finishedAt}
where task_id = #{taskId}
and status = 'RUNNING'
</update>
<update id="markFailed">
update ar_bill_generate_task
set status = 'FAILED',
error_message = #{errorMessage},
finished_at = #{finishedAt},
heartbeat_time = #{finishedAt}
where task_id = #{taskId}
and status in ('WAITING', 'RUNNING', 'CANCEL_REQUESTED')
</update>
<update id="markCancelRequested">
update ar_bill_generate_task
set status = 'CANCEL_REQUESTED',
heartbeat_time = sysdate()
where task_id = #{taskId}
and status in ('WAITING', 'RUNNING')
</update>
<update id="markCanceled">
update ar_bill_generate_task
set status = 'CANCELED',
error_message = '用户取消账单生成',
finished_at = #{finishedAt},
heartbeat_time = #{finishedAt}
where task_id = #{taskId}
and status in ('WAITING', 'RUNNING', 'CANCEL_REQUESTED')
</update>
<!-- 执行节点宕机时恢复无心跳的 RUNNING 任务,供调度器重新投递。 -->
<update id="resetExpiredRunning">
update ar_bill_generate_task
set status = 'WAITING',
error_message = '任务执行节点已重启,已自动恢复等待处理',
heartbeat_time = null
where status = 'RUNNING'
and heartbeat_time &lt; #{expiredBefore}
</update>
<!-- 取消请求长时间未完成时由补偿任务取出并执行数据清理。 -->
<select id="selectCancelRequestedBefore" resultMap="ArBillGenerateTaskResult">
<include refid="columns"/>
where status = 'CANCEL_REQUESTED'
and coalesce(heartbeat_time, create_time) &lt; #{expiredBefore}
order by create_time asc, task_id asc
limit #{limit}
</select>
</mapper>