Files
education/backend/roomroot-jwgl/target/classes/com/roomroot/jwgl/mapper/AccountManagementMapper.xml
T
2026-08-21 14:39:03 +08:00

1028 lines
45 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.roomroot.jwgl.mapper.AccountManagementMapper">
<!-- 将登录信息和账户管理状态显式映射为内部持久化记录。 -->
<resultMap id="AccountRecordResultMap"
type="com.roomroot.jwgl.entity.accountmanagement.AccountRecord">
<id column="编号" property="id"/>
<result column="用户密码" property="password"/>
<result column="用户姓名" property="userName"/>
<result column="密码提示问题" property="passwordQuestion"/>
<result column="密码回答问题" property="passwordAnswer"/>
<result column="电话" property="phone"/>
<result column="上次登录时间" property="lastLoginAt"/>
<result column="本次登录时间" property="currentLoginAt"/>
<result column="失败次数" property="failedAttempts"/>
<result column="失败时间" property="failedAt"/>
<result column="身份证" property="idCard"/>
<result column="账号" property="loginName"/>
<result column="统一账号" property="unifiedAccount"/>
<result column="自我介绍" property="introduction"/>
<result column="账户启用状态" property="enabled"/>
<result column="账户停用原因" property="disabledReason"/>
<result column="账户停用时间" property="disabledAt"/>
<result column="合并目标账户编号" property="mergedToAccountId"/>
<result column="账户合并时间" property="mergedAt"/>
</resultMap>
<!-- 将账户已有身份绑定映射为角色 VO。 -->
<resultMap id="AccountRoleResultMap"
type="com.roomroot.jwgl.vo.accountmanagement.AccountRoleVO">
<result column="roleType" property="roleType"/>
<result column="targetId" property="targetId"/>
<result column="targetName" property="targetName"/>
<result column="dataScope" property="dataScope"/>
<result column="enabled" property="enabled"/>
</resultMap>
<!-- 将批量查询角色映射为带所属账户编号的内部持久化记录。 -->
<resultMap id="AccountRoleRecordResultMap"
type="com.roomroot.jwgl.entity.accountmanagement.AccountRoleRecord">
<result column="accountId" property="accountId"/>
<result column="roleType" property="roleType"/>
<result column="targetId" property="targetId"/>
<result column="targetName" property="targetName"/>
<result column="dataScope" property="dataScope"/>
<result column="enabled" property="enabled"/>
</resultMap>
<!-- 将可选身份及其当前账户绑定情况映射为角色选项 VO。 -->
<resultMap id="AccountRoleOptionResultMap"
type="com.roomroot.jwgl.vo.accountmanagement.AccountRoleOptionVO">
<result column="roleType" property="roleType"/>
<result column="targetId" property="targetId"/>
<result column="targetName" property="targetName"/>
<result column="dataScope" property="dataScope"/>
<result column="enabled" property="enabled"/>
<result column="boundAccountId" property="boundAccountId"/>
<result column="boundLoginName" property="boundLoginName"/>
</resultMap>
<!-- 将 SSO 会话字段显式映射为项目现有的统一身份认证实体。 -->
<resultMap id="AccountSsoSessionResultMap"
type="com.roomroot.jwgl.entity.SSOUserAuthSession">
<id column="SessionKey" property="sessionKey"/>
<result column="AppKey" property="appKey"/>
<result column="LoginID" property="loginID"/>
<result column="UserID" property="userID"/>
<result column="UserType" property="userType"/>
<result column="IpAddress" property="ipAddress"/>
<result column="InvalidTime" property="invalidTime"/>
<result column="CreateTime" property="createTime"/>
</resultMap>
<!--
只映射账户临时锁定需要的两个系统参数。
使用现有 DBVersion 实体承载内部查询结果,不新增请求 DTO 或返回 VO。
-->
<resultMap id="AccountLoginSecurityPolicyResultMap"
type="com.roomroot.jwgl.entity.DBVersion">
<result column="allowedAttempts" property="yXMMCSCS"/>
<result column="lockMinutes" property="yHSDFZS"/>
</resultMap>
<!-- 将登录记录表字段映射为账户管理专用登录历史 VO。 -->
<resultMap id="AccountLoginHistoryResultMap"
type="com.roomroot.jwgl.vo.accountmanagement.AccountLoginHistoryVO">
<id column="编号" property="id"/>
<result column="登录时间" property="loginAt"/>
<result column="IP地址" property="ipAddress"/>
<result column="内网IP" property="intranetIpAddress"/>
<result column="事务ID" property="transactionId"/>
</resultMap>
<!-- 账户基础查询统一使用的字段清单。 -->
<sql id="AccountColumns">
d."编号", d."用户密码", d."用户姓名", d."密码提示问题",
d."密码回答问题", d."电话", d."上次登录时间", d."本次登录时间",
d."失败次数", d."失败时间", d."身份证", d."账号",
d."统一账号", d."自我介绍",
CASE
WHEN s."登录信息编号" IS NULL THEN 1
ELSE s."启用状态"
END AS "账户启用状态",
s."停用原因" AS "账户停用原因",
s."停用时间" AS "账户停用时间",
s."合并目标登录信息编号" AS "合并目标账户编号",
s."合并时间" AS "账户合并时间"
</sql>
<!--
账户分页查询统一使用的动态筛选条件。
关键字保持原有语义,可匹配账户资料和七类角色身份资料。
-->
<sql id="AccountFilters">
<if test="enabled != null">
AND CASE
WHEN s."登录信息编号" IS NULL THEN 1
ELSE s."启用状态"
END =
CASE WHEN #{enabled} = 1 THEN 1 ELSE 0 END
</if>
<if test="roleType != null">
AND EXISTS (
SELECT 1
FROM (
<include refid="AccountRoleRows"/>
) role_filter
WHERE role_filter."accountId" = d."编号"
AND role_filter."roleType" = #{roleType}
<if test="targetId != null">
AND role_filter."targetId" = #{targetId}
</if>
)
</if>
<if test="keyword != null">
AND (
LOWER(COALESCE(d."账号", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(d."用户姓名", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(d."统一账号", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(d."电话", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(d."身份证", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR EXISTS (
SELECT 1
FROM (
<include refid="AccountRoleRows"/>
) keyword_role
WHERE keyword_role."accountId" = d."编号"
AND (
LOWER(COALESCE(keyword_role."roleType", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(keyword_role."roleName", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(keyword_role."targetId", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(keyword_role."targetName", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(keyword_role."dataScope", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
)
)
)
</if>
</sql>
<!--
分页查询账户,旧账户没有状态记录时按启用处理。
MyBatis-Plus 分页拦截器负责生成达梦数据库的总数和分页语句。
-->
<select id="selectAccountPage" resultMap="AccountRecordResultMap">
SELECT
<include refid="AccountColumns"/>
FROM "登录信息表" d
LEFT JOIN "账户状态表" s
ON s."登录信息编号" = d."编号"
<where>
<include refid="AccountFilters"/>
</where>
ORDER BY
CASE WHEN d."账号" IS NULL THEN 1 ELSE 0 END,
LOWER(d."账号"),
d."账号",
CASE WHEN d."用户姓名" IS NULL THEN 1 ELSE 0 END,
LOWER(d."用户姓名"),
d."用户姓名",
d."编号"
</select>
<!-- 根据登录信息编号查询单个账户。 -->
<select id="selectAccountById" resultMap="AccountRecordResultMap">
SELECT
<include refid="AccountColumns"/>
FROM "登录信息表" d
LEFT JOIN "账户状态表" s
ON s."登录信息编号" = d."编号"
WHERE d."编号" = #{id}
</select>
<!--
第一步:按登录账号查询完整账户内部记录,密码摘要仅用于业务层校验。
第二步:忽略登录账号首尾空格和英文字母大小写,保持与唯一性校验规则一致。
第三步:旧账户没有状态记录时按启用处理,停用和合并状态由业务层继续判断。
-->
<select id="selectAccountByLoginName"
resultMap="AccountRecordResultMap">
SELECT
<include refid="AccountColumns"/>
FROM "登录信息表" d
LEFT JOIN "账户状态表" s
ON s."登录信息编号" = d."编号"
WHERE LOWER(TRIM(d."账号"))
= LOWER(TRIM(#{loginName}))
ORDER BY d."编号"
</select>
<!--
第一步:从系统版本表中读取允许密码尝试次数和用户锁定分钟数。
第二步:使用最大 ID 取得最新配置,兼容系统版本表保留多条历史记录的情况。
第三步:通过固定英文别名映射到现有实体字段,避免依赖旧 Mapper 的乱码属性名。
-->
<select id="selectLoginSecurityPolicy"
resultMap="AccountLoginSecurityPolicyResultMap">
SELECT
currentVersion."允许密码尝试次数" AS "allowedAttempts",
currentVersion."用户锁定分钟数" AS "lockMinutes"
FROM "DBVersion" currentVersion
WHERE currentVersion."ID" = (
SELECT MAX(historyVersion."ID")
FROM "DBVersion" historyVersion
)
</select>
<!-- 登录账号按去除首尾空格和忽略大小写的方式检查重复。 -->
<select id="countByLoginName" resultType="int">
SELECT COUNT(1)
FROM "登录信息表"
WHERE LOWER(TRIM("账号")) = LOWER(#{loginName})
<if test="excludeId != null">
AND "编号" != #{excludeId}
</if>
</select>
<!-- 非空统一账号按去除首尾空格和忽略大小写的方式检查重复。 -->
<select id="countByUnifiedAccount" resultType="int">
SELECT COUNT(1)
FROM "登录信息表"
WHERE "统一账号" IS NOT NULL
AND LOWER(TRIM("统一账号")) = LOWER(#{unifiedAccount})
<if test="excludeId != null">
AND "编号" != #{excludeId}
</if>
</select>
<!-- 新增账户时保存业务层生成的 BCrypt 密码摘要,并初始化登录时间和失败次数。 -->
<insert id="insertAccount">
INSERT INTO "登录信息表" (
"编号", "用户密码", "用户姓名", "密码提示问题",
"密码回答问题", "电话", "上次登录时间", "本次登录时间",
"失败次数", "失败时间", "身份证", "账号",
"统一账号", "自我介绍", "照片"
) VALUES (
#{account.id}, #{account.password}, #{account.userName},
#{account.passwordQuestion}, #{account.passwordAnswer},
#{account.phone}, #{account.lastLoginAt}, #{account.currentLoginAt},
#{account.failedAttempts}, #{account.failedAt}, #{account.idCard},
#{account.loginName}, #{account.unifiedAccount},
#{account.introduction}, ''
)
</insert>
<!-- 普通修改只更新账户资料,不隐式改变密码、启停状态或登录统计。 -->
<update id="updateAccount">
UPDATE "登录信息表"
SET "用户姓名" = #{account.userName},
"密码提示问题" = #{account.passwordQuestion},
"密码回答问题" = #{account.passwordAnswer},
"电话" = #{account.phone},
"身份证" = #{account.idCard},
"账号" = #{account.loginName},
"统一账号" = #{account.unifiedAccount},
"自我介绍" = #{account.introduction}
WHERE "编号" = #{account.id}
</update>
<!-- 使用业务层生成的 BCrypt 密码摘要重置密码,并同时清除临时登录失败状态。 -->
<update id="resetPassword">
UPDATE "登录信息表"
SET "用户密码" = #{password},
"失败次数" = 0,
"失败时间" = CURRENT_TIMESTAMP
WHERE "编号" = #{id}
</update>
<!--
第一步:在数据库当前失败次数基础上原子加一,避免并发登录丢失失败记录。
第二步:旧账户失败次数为空时按零次处理。
第三步:同步保存本次密码校验失败时间,供后续临时锁定功能判断。
-->
<update id="recordLoginFailure">
UPDATE "登录信息表"
SET "失败次数" = COALESCE("失败次数", 0) + 1,
"失败时间" = #{failedAt}
WHERE "编号" = #{id}
</update>
<!--
第一步:只处理已经达到允许尝试次数的账户,普通失败记录不执行清理。
第二步:只清理失败时间早于锁定截止点的记录,锁定期内的新记录保持不变。
第三步:同时清零失败次数和失败时间,使锁定到期后的下一次密码尝试重新计数。
-->
<update id="clearExpiredLoginFailureState">
UPDATE "登录信息表"
SET "失败次数" = 0,
"失败时间" = #{expiredBefore}
WHERE "编号" = #{id}
AND COALESCE("失败次数", 0) &gt;= #{allowedAttempts}
AND "失败时间" IS NOT NULL
AND "失败时间" &lt;= #{expiredBefore}
</update>
<!--
第一步:把原本的本次登录时间转存为上次登录时间。
第二步:写入当前登录成功时间。
第三步:成功认证后清零连续失败次数和最近失败时间。
-->
<update id="recordLoginSuccess">
UPDATE "登录信息表"
SET "上次登录时间" = "本次登录时间",
"本次登录时间" = #{loginAt},
"失败次数" = 0,
"失败时间" = #{loginAt}
WHERE "编号" = #{id}
</update>
<!--
第一步:使用业务层生成的 UUID 保存登录记录主键。
第二步:登录信息编号、登录时间和来源 IP 与本次成功登录保持一致。
第三步:服务端无法可靠取得客户端内网 IP 时允许写入空值。
第四步:独立事务标识用于后续登录审计和问题定位。
-->
<insert id="insertLoginHistory">
INSERT INTO "登录记录表" (
"编号", "登录信息编号", "登录时间",
"IP地址", "内网IP", "事务ID"
) VALUES (
#{id}, #{accountId}, #{loginAt},
#{ipAddress}, #{intranetIpAddress}, #{transactionId}
)
</insert>
<!--
第一步:只查询当前会话对应账户的登录记录,防止查看其他账户历史。
第二步:登录时间按最近优先排列,相同时间使用记录编号稳定排序。
第三步:分页语句和总记录数由 MyBatis-Plus 分页拦截器统一生成。
-->
<select id="selectLoginHistoryPage"
resultMap="AccountLoginHistoryResultMap">
SELECT "编号", "登录时间", "IP地址", "内网IP", "事务ID"
FROM "登录记录表"
WHERE "登录信息编号" = #{accountId}
ORDER BY "登录时间" DESC, "编号" DESC
</select>
<!--
根据登录账号删除全部在线会话。
忽略大小写和账号首尾空格,保持与账户唯一性校验规则一致。
-->
<delete id="deleteOnlineSessionsByLoginName">
DELETE FROM "MemberOnlineInfo"
WHERE LOWER(TRIM("账号")) = LOWER(#{loginName})
</delete>
<!-- 根据 Servlet 会话编号删除旧的在线记录,避免会话替换后残留旧身份。 -->
<delete id="deleteOnlineSessionBySessionId">
DELETE FROM "MemberOnlineInfo"
WHERE "SessionId" = #{sessionId}
</delete>
<!--
第一步:使用新建 Servlet 会话编号作为在线记录主键。
第二步:保存当前账户、SSO 角色类别和访问来源。
第三步:开始访问时间和最后访问时间使用同一时间点初始化。
-->
<insert id="insertOnlineSession">
INSERT INTO "MemberOnlineInfo" (
"SessionId", "账号", "姓名", "单位", "角色类别", "IP",
"开始访问时间", "最后访问时间", "最后访问路径"
) VALUES (
#{sessionId}, #{loginName}, #{userName}, #{unit},
#{roleCategory}, #{ipAddress}, #{startAccessTime},
#{lastAccessTime}, #{lastAccessPath}
)
</insert>
<!--
第一步:根据当前 Servlet 会话定位在线用户记录。
第二步:通过登录账号关联登录信息表。
第三步:只返回当前会话对应的账户编号,供停用自我保护使用。
-->
<select id="selectAccountIdBySessionId"
resultType="string">
SELECT account."编号"
FROM "MemberOnlineInfo" moi
INNER JOIN "登录信息表" account
ON LOWER(TRIM(account."账号"))
= LOWER(TRIM(moi."账号"))
WHERE moi."SessionId" = #{sessionId}
</select>
<!--
第一步:使用 SSO 会话主键执行精确查询。
第二步:返回会话所属应用、两个身份候选值和有效期等校验字段。
第三步:会话不存在时不返回记录,由业务层转换为明确的资源不存在提示。
-->
<select id="selectSsoSessionBySessionKey"
resultMap="AccountSsoSessionResultMap">
SELECT "SessionKey", "AppKey", "LoginID", "UserID",
"UserType", "IpAddress", "InvalidTime", "CreateTime"
FROM "SSO_UserAuthSession"
WHERE "SessionKey" = #{sessionKey}
</select>
<!--
第一步:仅在登录信息表的统一账号字段中匹配 SSO 身份候选值。
第二步:统一去除首尾空格并忽略英文字母大小写,保持与账户唯一索引一致。
第三步:联查账户管理状态;旧账户没有状态记录时仍按启用处理。
第四步:只读取账户解析所需的非敏感字段,并按账户编号稳定排序。
-->
<select id="selectAccountsByUnifiedAccounts"
resultMap="AccountRecordResultMap">
SELECT d."编号", d."用户姓名", d."账号", d."统一账号",
CASE
WHEN s."登录信息编号" IS NULL THEN 1
ELSE s."启用状态"
END AS "账户启用状态",
s."停用原因" AS "账户停用原因",
s."停用时间" AS "账户停用时间",
s."合并目标登录信息编号" AS "合并目标账户编号",
s."合并时间" AS "账户合并时间"
FROM "登录信息表" d
LEFT JOIN "账户状态表" s
ON s."登录信息编号" = d."编号"
WHERE d."统一账号" IS NOT NULL
AND LOWER(TRIM(d."统一账号")) IN
<foreach collection="unifiedAccounts"
item="unifiedAccount"
open="("
separator=","
close=")">
LOWER(#{unifiedAccount})
</foreach>
ORDER BY d."编号"
</select>
<!-- 查询账户是否已经具有专用管理状态记录。 -->
<select id="countAccountStatus" resultType="int">
SELECT COUNT(1)
FROM "账户状态表"
WHERE "登录信息编号" = #{accountId}
</select>
<!-- 新增账户专用启停和合并状态。 -->
<insert id="insertAccountStatus">
INSERT INTO "账户状态表" (
"登录信息编号", "启用状态", "停用原因", "停用时间",
"合并目标登录信息编号", "合并时间"
) VALUES (
#{account.id},
CASE WHEN #{account.enabled} = 1 THEN 1 ELSE 0 END,
#{account.disabledReason}, #{account.disabledAt},
#{account.mergedToAccountId}, #{account.mergedAt}
)
</insert>
<!-- 修改账户专用启停和合并状态。 -->
<update id="updateAccountStatus">
UPDATE "账户状态表"
SET "启用状态" =
CASE WHEN #{account.enabled} = 1 THEN 1 ELSE 0 END,
"停用原因" = #{account.disabledReason},
"停用时间" = #{account.disabledAt},
"合并目标登录信息编号" = #{account.mergedToAccountId},
"合并时间" = #{account.mergedAt}
WHERE "登录信息编号" = #{account.id}
</update>
<!--
七类账户角色统一查询行。
同时保留所属账户编号用于批量分组,角色中文名称用于关键字筛选。
-->
<sql id="AccountRoleRows">
SELECT b."登录信息编号" AS "accountId",
'DEPARTMENT_PERSONNEL' AS "roleType",
'机关人员' AS "roleName",
p."编号" AS "targetId",
COALESCE(p."用户姓名", p."编号") AS "targetName",
COALESCE(dep."机关名称", p."实施_机关编号") AS "dataScope",
CASE
WHEN COALESCE(p."离职状态", 0) = 0
AND COALESCE(dep."停用", 0) = 0 THEN 1
ELSE 0
END AS "enabled"
FROM "机关人员登录表" b
JOIN "实施_机关参谋" p ON p."编号" = b."机关人员编号"
LEFT JOIN "实施_机关" dep ON dep."编号" = p."实施_机关编号"
UNION ALL
SELECT b."登录信息编号" AS "accountId",
'TEACHER' AS "roleType",
'教员' AS "roleName",
t."教员编号" AS "targetId",
t."教员姓名" AS "targetName",
COALESCE(o."教研室名称", t."教研室代号") AS "dataScope",
CASE
WHEN COALESCE(t."离职状态", 0) = 0
AND COALESCE(o."停用", 0) = 0 THEN 1
ELSE 0
END AS "enabled"
FROM "教员登录表" b
JOIN "教员表" t ON t."教员编号" = b."教员编号"
LEFT JOIN "教研室表" o ON o."教研室代号" = t."教研室代号"
UNION ALL
SELECT b."登录信息编号" AS "accountId",
'RESEARCH_OFFICE' AS "roleType",
'教研室' AS "roleName",
o."教研室代号" AS "targetId",
o."教研室名称" AS "targetName",
COALESCE(dep."机关名称", o."部系") AS "dataScope",
CASE WHEN COALESCE(o."停用", 0) = 0 THEN 1 ELSE 0 END
AS "enabled"
FROM "教研室登录表" b
JOIN "教研室表" o ON o."教研室代号" = b."教研室代号"
LEFT JOIN "实施_机关_教研室" r
ON r."教研室编号" = o."教研室代号"
LEFT JOIN "实施_机关" dep ON dep."编号" = r."实施_机关编号"
UNION ALL
SELECT b."登录信息编号" AS "accountId",
'TEACHING_CLASS' AS "roleType",
'教学班次' AS "roleName",
c."学员队编号" AS "targetId",
c."学员队名称" AS "targetName",
COALESCE(dep."机关名称", c."所属单位") AS "dataScope",
1 AS "enabled"
FROM "学员队登录表" b
JOIN "学员队表" c ON c."学员队编号" = b."学员队编号"
LEFT JOIN "实施_机关_学员队" r
ON r."学员队编号" = c."学员队编号"
LEFT JOIN "实施_机关" dep ON dep."编号" = r."实施_机关编号"
UNION ALL
SELECT b."登录信息编号" AS "accountId",
'INSPECTION_ADMINISTRATOR' AS "roleType",
'听查课管理员' AS "roleName",
a."编号" AS "targetId",
a."姓名" AS "targetName",
NULL AS "dataScope",
1 AS "enabled"
FROM "听查课管理员登录表" b
JOIN "听查课管理员" a
ON a."编号" = b."听查课管理员编号"
UNION ALL
SELECT s."登录信息编号" AS "accountId",
'STUDENT' AS "roleType",
'学员' AS "roleName",
s."编号" AS "targetId",
s."姓名" AS "targetName",
COALESCE(c."学员队名称", s."当前班次编号") AS "dataScope",
CASE WHEN COALESCE(s."退学状态", 0) = 0 THEN 1 ELSE 0 END
AS "enabled"
FROM "学员信息表" s
LEFT JOIN "学员队表" c ON c."学员队编号" = s."当前班次编号"
UNION ALL
SELECT b."登录信息编号" AS "accountId",
'LEGACY_USER' AS "roleType",
'旧用户' AS "roleName",
u."用户帐号" AS "targetId",
COALESCE(u."用户姓名", u."用户帐号") AS "targetName",
u."教学管理机构编号" AS "dataScope",
1 AS "enabled"
FROM "用户登录表" b
JOIN "用户表" u ON u."用户帐号" = b."用户账号"
</sql>
<!-- 查询单个账户全部角色,具体角色名称由业务层统一补充。 -->
<select id="selectRolesByAccountId" resultMap="AccountRoleResultMap">
SELECT role_data."roleType",
role_data."targetId",
role_data."targetName",
role_data."dataScope",
role_data."enabled"
FROM (
<include refid="AccountRoleRows"/>
) role_data
WHERE role_data."accountId" = #{accountId}
</select>
<!-- 一次性查询当前页账户角色,避免逐账户查询产生 N+1 访问。 -->
<select id="selectRoleRecordsByAccountIds"
resultMap="AccountRoleRecordResultMap">
SELECT role_data."accountId",
role_data."roleType",
role_data."targetId",
role_data."targetName",
role_data."dataScope",
role_data."enabled"
FROM (
<include refid="AccountRoleRows"/>
) role_data
WHERE role_data."accountId" IN
<foreach collection="accountIds"
item="accountId"
open="("
separator=","
close=")">
#{accountId}
</foreach>
</select>
<!-- 汇总七类角色身份及其当前账户绑定,供分页查询和精确校验共同复用。 -->
<sql id="AccountRoleOptionRows">
<choose>
<when test="roleType == 'DEPARTMENT_PERSONNEL'">
SELECT 'DEPARTMENT_PERSONNEL' AS "roleType",
p."编号" AS "targetId",
COALESCE(p."用户姓名", p."编号") AS "targetName",
COALESCE(dep."机关名称", p."实施_机关编号") AS "dataScope",
CASE
WHEN COALESCE(p."离职状态", 0) = 0
AND COALESCE(dep."停用", 0) = 0 THEN 1
ELSE 0
END AS "enabled",
b."登录信息编号" AS "boundAccountId",
d."账号" AS "boundLoginName"
FROM "实施_机关参谋" p
LEFT JOIN "实施_机关" dep
ON dep."编号" = p."实施_机关编号"
LEFT JOIN "机关人员登录表" b
ON b."机关人员编号" = p."编号"
LEFT JOIN "登录信息表" d
ON d."编号" = b."登录信息编号"
</when>
<when test="roleType == 'TEACHER'">
SELECT 'TEACHER' AS "roleType",
t."教员编号" AS "targetId",
t."教员姓名" AS "targetName",
COALESCE(o."教研室名称", t."教研室代号") AS "dataScope",
CASE
WHEN COALESCE(t."离职状态", 0) = 0
AND COALESCE(o."停用", 0) = 0 THEN 1
ELSE 0
END AS "enabled",
b."登录信息编号" AS "boundAccountId",
d."账号" AS "boundLoginName"
FROM "教员表" t
LEFT JOIN "教研室表" o
ON o."教研室代号" = t."教研室代号"
LEFT JOIN "教员登录表" b
ON b."教员编号" = t."教员编号"
LEFT JOIN "登录信息表" d
ON d."编号" = b."登录信息编号"
</when>
<when test="roleType == 'RESEARCH_OFFICE'">
SELECT 'RESEARCH_OFFICE' AS "roleType",
o."教研室代号" AS "targetId",
o."教研室名称" AS "targetName",
COALESCE(dep."机关名称", o."部系") AS "dataScope",
CASE WHEN COALESCE(o."停用", 0) = 0 THEN 1 ELSE 0 END
AS "enabled",
b."登录信息编号" AS "boundAccountId",
d."账号" AS "boundLoginName"
FROM "教研室表" o
LEFT JOIN "实施_机关_教研室" r
ON r."教研室编号" = o."教研室代号"
LEFT JOIN "实施_机关" dep
ON dep."编号" = r."实施_机关编号"
LEFT JOIN "教研室登录表" b
ON b."教研室代号" = o."教研室代号"
LEFT JOIN "登录信息表" d
ON d."编号" = b."登录信息编号"
</when>
<when test="roleType == 'TEACHING_CLASS'">
SELECT 'TEACHING_CLASS' AS "roleType",
c."学员队编号" AS "targetId",
c."学员队名称" AS "targetName",
COALESCE(dep."机关名称", c."所属单位") AS "dataScope",
1 AS "enabled",
b."登录信息编号" AS "boundAccountId",
d."账号" AS "boundLoginName"
FROM "学员队表" c
LEFT JOIN "实施_机关_学员队" r
ON r."学员队编号" = c."学员队编号"
LEFT JOIN "实施_机关" dep
ON dep."编号" = r."实施_机关编号"
LEFT JOIN "学员队登录表" b
ON b."学员队编号" = c."学员队编号"
LEFT JOIN "登录信息表" d
ON d."编号" = b."登录信息编号"
</when>
<when test="roleType == 'INSPECTION_ADMINISTRATOR'">
SELECT 'INSPECTION_ADMINISTRATOR' AS "roleType",
a."编号" AS "targetId",
a."姓名" AS "targetName",
NULL AS "dataScope",
1 AS "enabled",
b."登录信息编号" AS "boundAccountId",
d."账号" AS "boundLoginName"
FROM "听查课管理员" a
LEFT JOIN "听查课管理员登录表" b
ON b."听查课管理员编号" = a."编号"
LEFT JOIN "登录信息表" d
ON d."编号" = b."登录信息编号"
</when>
<when test="roleType == 'STUDENT'">
SELECT 'STUDENT' AS "roleType",
s."编号" AS "targetId",
s."姓名" AS "targetName",
COALESCE(c."学员队名称", s."当前班次编号") AS "dataScope",
CASE WHEN COALESCE(s."退学状态", 0) = 0 THEN 1 ELSE 0 END
AS "enabled",
s."登录信息编号" AS "boundAccountId",
d."账号" AS "boundLoginName"
FROM "学员信息表" s
LEFT JOIN "学员队表" c
ON c."学员队编号" = s."当前班次编号"
LEFT JOIN "登录信息表" d
ON d."编号" = s."登录信息编号"
</when>
<when test="roleType == 'LEGACY_USER'">
SELECT 'LEGACY_USER' AS "roleType",
u."用户帐号" AS "targetId",
COALESCE(u."用户姓名", u."用户帐号") AS "targetName",
u."教学管理机构编号" AS "dataScope",
1 AS "enabled",
b."登录信息编号" AS "boundAccountId",
d."账号" AS "boundLoginName"
FROM "用户表" u
LEFT JOIN "用户登录表" b
ON b."用户账号" = u."用户帐号"
LEFT JOIN "登录信息表" d
ON d."编号" = b."登录信息编号"
</when>
<otherwise>
SELECT NULL AS "roleType", NULL AS "targetId",
NULL AS "targetName", NULL AS "dataScope",
0 AS "enabled", NULL AS "boundAccountId",
NULL AS "boundLoginName"
FROM "登录信息表"
WHERE 1 = 0
</otherwise>
</choose>
</sql>
<!--
第一步:在数据库中筛选指定角色类型的候选身份。
第二步:通过 ROW_NUMBER 按角色类型和身份编号消除历史异常重复行。
第三步:按照身份名称、数据范围和身份编号稳定排序后执行分页。
-->
<select id="selectRoleOptionPage"
resultMap="AccountRoleOptionResultMap">
SELECT ranked_options."roleType",
ranked_options."targetId",
ranked_options."targetName",
ranked_options."dataScope",
ranked_options."enabled",
ranked_options."boundAccountId",
ranked_options."boundLoginName"
FROM (
SELECT filtered_options."roleType",
filtered_options."targetId",
filtered_options."targetName",
filtered_options."dataScope",
filtered_options."enabled",
filtered_options."boundAccountId",
filtered_options."boundLoginName",
ROW_NUMBER() OVER (
PARTITION BY
filtered_options."roleType",
filtered_options."targetId"
ORDER BY
CASE
WHEN filtered_options."boundAccountId" IS NULL
THEN 1
ELSE 0
END,
CASE
WHEN filtered_options."targetName" IS NULL
THEN 1
ELSE 0
END,
LOWER(filtered_options."targetName"),
filtered_options."targetName",
CASE
WHEN filtered_options."dataScope" IS NULL
THEN 1
ELSE 0
END,
LOWER(filtered_options."dataScope"),
filtered_options."dataScope",
filtered_options."boundAccountId"
) AS "rowNumber"
FROM (
<include refid="AccountRoleOptionRows"/>
) filtered_options
WHERE filtered_options."targetId" IS NOT NULL
<if test="keyword != null">
AND (
LOWER(COALESCE(filtered_options."targetId", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(filtered_options."targetName", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(filtered_options."dataScope", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
OR LOWER(COALESCE(
filtered_options."boundLoginName", ''))
LIKE LOWER(CONCAT('%', #{keyword}, '%'))
)
</if>
<if test="unboundOnly != null and unboundOnly">
AND (
filtered_options."boundAccountId" IS NULL
<if test="accountId != null">
OR filtered_options."boundAccountId" = #{accountId}
</if>
)
</if>
) ranked_options
WHERE ranked_options."rowNumber" = 1
ORDER BY
CASE
WHEN ranked_options."targetName" IS NULL THEN 1
ELSE 0
END,
LOWER(ranked_options."targetName"),
ranked_options."targetName",
CASE
WHEN ranked_options."dataScope" IS NULL THEN 1
ELSE 0
END,
LOWER(ranked_options."dataScope"),
ranked_options."dataScope",
ranked_options."targetId"
</select>
<!-- 根据角色类型和身份编号精确查询身份,供角色写入前校验使用。 -->
<select id="selectRoleOptions" resultMap="AccountRoleOptionResultMap">
SELECT role_options."roleType",
role_options."targetId",
role_options."targetName",
role_options."dataScope",
role_options."enabled",
role_options."boundAccountId",
role_options."boundLoginName"
FROM (
<include refid="AccountRoleOptionRows"/>
) role_options
<if test="targetId != null">
WHERE role_options."targetId" = #{targetId}
</if>
</select>
<!--
按角色类型清空账户绑定。
学员身份直接保存在学员信息表,因此采用置空操作。
-->
<update id="deleteRoleBindings">
<choose>
<when test="roleType == 'DEPARTMENT_PERSONNEL'">
DELETE FROM "机关人员登录表"
WHERE "登录信息编号" = #{accountId}
</when>
<when test="roleType == 'TEACHER'">
DELETE FROM "教员登录表"
WHERE "登录信息编号" = #{accountId}
</when>
<when test="roleType == 'RESEARCH_OFFICE'">
DELETE FROM "教研室登录表"
WHERE "登录信息编号" = #{accountId}
</when>
<when test="roleType == 'TEACHING_CLASS'">
DELETE FROM "学员队登录表"
WHERE "登录信息编号" = #{accountId}
</when>
<when test="roleType == 'INSPECTION_ADMINISTRATOR'">
DELETE FROM "听查课管理员登录表"
WHERE "登录信息编号" = #{accountId}
</when>
<when test="roleType == 'STUDENT'">
UPDATE "学员信息表"
SET "登录信息编号" = NULL
WHERE "登录信息编号" = #{accountId}
</when>
<when test="roleType == 'LEGACY_USER'">
DELETE FROM "用户登录表"
WHERE "登录信息编号" = #{accountId}
</when>
<otherwise>
UPDATE "登录信息表"
SET "编号" = "编号"
WHERE 1 = 0
</otherwise>
</choose>
</update>
<!-- 按角色类型写入一条身份绑定。 -->
<update id="saveRoleBinding">
<choose>
<when test="roleType == 'DEPARTMENT_PERSONNEL'">
INSERT INTO "机关人员登录表" (
"机关人员编号", "登录信息编号"
) VALUES (
#{targetId}, #{accountId}
)
</when>
<when test="roleType == 'TEACHER'">
INSERT INTO "教员登录表" (
"教员编号", "登录信息编号"
) VALUES (
#{targetId}, #{accountId}
)
</when>
<when test="roleType == 'RESEARCH_OFFICE'">
INSERT INTO "教研室登录表" (
"教研室代号", "登录信息编号"
) VALUES (
#{targetId}, #{accountId}
)
</when>
<when test="roleType == 'TEACHING_CLASS'">
INSERT INTO "学员队登录表" (
"学员队编号", "登录信息编号"
) VALUES (
#{targetId}, #{accountId}
)
</when>
<when test="roleType == 'INSPECTION_ADMINISTRATOR'">
INSERT INTO "听查课管理员登录表" (
"听查课管理员编号", "登录信息编号"
) VALUES (
#{targetId}, #{accountId}
)
</when>
<when test="roleType == 'STUDENT'">
UPDATE "学员信息表"
SET "登录信息编号" = #{accountId}
WHERE "编号" = #{targetId}
AND "登录信息编号" IS NULL
</when>
<when test="roleType == 'LEGACY_USER'">
INSERT INTO "用户登录表" (
"用户账号", "登录信息编号"
) VALUES (
#{targetId}, #{accountId}
)
</when>
<otherwise>
UPDATE "登录信息表"
SET "编号" = "编号"
WHERE 1 = 0
</otherwise>
</choose>
</update>
<!-- 按角色类型将源账户身份原子迁移到目标账户。 -->
<update id="moveRoleBindings">
<choose>
<when test="roleType == 'DEPARTMENT_PERSONNEL'">
UPDATE "机关人员登录表"
SET "登录信息编号" = #{targetAccountId}
WHERE "登录信息编号" = #{sourceAccountId}
</when>
<when test="roleType == 'TEACHER'">
UPDATE "教员登录表"
SET "登录信息编号" = #{targetAccountId}
WHERE "登录信息编号" = #{sourceAccountId}
</when>
<when test="roleType == 'RESEARCH_OFFICE'">
UPDATE "教研室登录表"
SET "登录信息编号" = #{targetAccountId}
WHERE "登录信息编号" = #{sourceAccountId}
</when>
<when test="roleType == 'TEACHING_CLASS'">
UPDATE "学员队登录表"
SET "登录信息编号" = #{targetAccountId}
WHERE "登录信息编号" = #{sourceAccountId}
</when>
<when test="roleType == 'INSPECTION_ADMINISTRATOR'">
UPDATE "听查课管理员登录表"
SET "登录信息编号" = #{targetAccountId}
WHERE "登录信息编号" = #{sourceAccountId}
</when>
<when test="roleType == 'STUDENT'">
UPDATE "学员信息表"
SET "登录信息编号" = #{targetAccountId}
WHERE "登录信息编号" = #{sourceAccountId}
</when>
<when test="roleType == 'LEGACY_USER'">
UPDATE "用户登录表"
SET "登录信息编号" = #{targetAccountId}
WHERE "登录信息编号" = #{sourceAccountId}
</when>
<otherwise>
UPDATE "登录信息表"
SET "编号" = "编号"
WHERE 1 = 0
</otherwise>
</choose>
</update>
</mapper>