1028 lines
45 KiB
XML
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) >= #{allowedAttempts}
|
|
AND "失败时间" IS NOT NULL
|
|
AND "失败时间" <= #{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>
|