From 9742e0fbfe4611d12a1b17c5c17845e6e5be7fa3 Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Sat, 4 Jul 2026 17:02:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(iam):=20=E7=94=A8=E6=88=B7=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E5=8F=B0=E5=BF=AB=E6=8D=B7=E5=85=A5=E5=8F=A3=E5=81=8F?= =?UTF-8?q?=E5=A5=BD(=E6=98=BE=E9=9A=90+=E6=8E=92=E5=BA=8F)=20+=20?= =?UTF-8?q?=E5=BB=BA=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _config/sql/update-tables.sql | 31 ++++++++++ .../UserDashboardPreferenceController.java | 44 ++++++++++++++ .../UserDashboardPreferenceConvert.java | 15 +++++ .../UserDashboardPreferenceManager.java | 22 +++++++ .../UserDashboardPreferenceMapper.java | 12 ++++ .../UserDashboardPreference.java | 50 ++++++++++++++++ .../QuickEntrySaveParam.java | 18 ++++++ .../dashboardpreference/QuickEntryResult.java | 18 ++++++ .../UserDashboardPreferenceService.java | 58 +++++++++++++++++++ 9 files changed, 268 insertions(+) create mode 100644 daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/controller/dashboardpreference/UserDashboardPreferenceController.java create mode 100644 daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/convert/dashboardpreference/UserDashboardPreferenceConvert.java create mode 100644 daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/dao/dashboardpreference/UserDashboardPreferenceManager.java create mode 100644 daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/dao/dashboardpreference/UserDashboardPreferenceMapper.java create mode 100644 daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/entity/dashboardpreference/UserDashboardPreference.java create mode 100644 daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/param/dashboardpreference/QuickEntrySaveParam.java create mode 100644 daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/dashboardpreference/QuickEntryResult.java create mode 100644 daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/dashboardpreference/UserDashboardPreferenceService.java diff --git a/_config/sql/update-tables.sql b/_config/sql/update-tables.sql index e69de29bb..037d3a2fa 100644 --- a/_config/sql/update-tables.sql +++ b/_config/sql/update-tables.sql @@ -0,0 +1,31 @@ +-- 用户工作台快捷入口偏好表: 用户个性化工作台快捷入口(显隐+排序), PC与移动按 client_code 分开管理 + +CREATE TABLE IF NOT EXISTS "public"."iam_user_dashboard_preference" ( + "id" bigint NOT NULL PRIMARY KEY, + "user_id" bigint NOT NULL, + "client_code" varchar(32) NOT NULL, + "entries" jsonb NOT NULL DEFAULT '[]'::jsonb, + "creator" bigint, + "create_time" timestamptz(6), + "last_modifier" bigint, + "last_modified_time" timestamptz(6), + "version" integer NOT NULL DEFAULT 0, + "deleted" boolean NOT NULL DEFAULT false +); + +COMMENT ON TABLE "public"."iam_user_dashboard_preference" IS '用户工作台快捷入口偏好'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."id" IS '主键'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."user_id" IS '用户ID'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."client_code" IS '终端编码(WEB/MOBILE), PC与移动分开管理'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."entries" IS '已选快捷入口有序序列(纯key数组), 如 ["merchant","notify"]'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."creator" IS '创建人ID'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."create_time" IS '创建时间(UTC)'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."last_modifier" IS '最后修改人ID'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."last_modified_time" IS '最后修改时间(UTC)'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."version" IS '乐观锁版本号'; +COMMENT ON COLUMN "public"."iam_user_dashboard_preference"."deleted" IS '逻辑删除标志'; + +-- 每用户每终端唯一一份配置(仅在未逻辑删除时生效, 允许删后重建) +CREATE UNIQUE INDEX IF NOT EXISTS "uk_user_das_pref_user_client" + ON "public"."iam_user_dashboard_preference" ("user_id", "client_code") + WHERE "deleted" = false; diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/controller/dashboardpreference/UserDashboardPreferenceController.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/controller/dashboardpreference/UserDashboardPreferenceController.java new file mode 100644 index 000000000..dc197714f --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/controller/dashboardpreference/UserDashboardPreferenceController.java @@ -0,0 +1,44 @@ +package cn.daxpay.open.platform.iam.controller.dashboardpreference; + +import cn.daxpay.open.platform.core.rest.Res; +import cn.daxpay.open.platform.core.rest.result.Result; +import cn.daxpay.open.platform.iam.param.dashboardpreference.QuickEntrySaveParam; +import cn.daxpay.open.platform.iam.result.dashboardpreference.QuickEntryResult; +import cn.daxpay.open.platform.iam.service.dashboardpreference.UserDashboardPreferenceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/// # 工作台快捷入口偏好 +/// +/// 用户个性化工作台快捷入口(显隐 + 排序), PC 与移动端按请求终端自动区分. +/// 每个登录用户均可管理自己的偏好, 无需额外权限码. +@Validated +@Tag(name = "工作台快捷入口") +@RestController +@RequestMapping("/iam/dashboard/quick-entry") +@RequiredArgsConstructor +public class UserDashboardPreferenceController { + + private final UserDashboardPreferenceService userDashboardPreferenceService; + + @Operation(summary = "查询当前用户的快捷入口序列") + @GetMapping + public Result find() { + return Res.ok(userDashboardPreferenceService.findCurrent()); + } + + @Operation(summary = "保存当前用户的快捷入口序列(整体覆盖)") + @PutMapping + public Result save(@RequestBody @Validated QuickEntrySaveParam param) { + userDashboardPreferenceService.saveCurrent(param.getEntries()); + return Res.ok(); + } + +} diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/convert/dashboardpreference/UserDashboardPreferenceConvert.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/convert/dashboardpreference/UserDashboardPreferenceConvert.java new file mode 100644 index 000000000..85c02073f --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/convert/dashboardpreference/UserDashboardPreferenceConvert.java @@ -0,0 +1,15 @@ +package cn.daxpay.open.platform.iam.convert.dashboardpreference; + +import cn.daxpay.open.platform.iam.entity.dashboardpreference.UserDashboardPreference; +import cn.daxpay.open.platform.iam.result.dashboardpreference.QuickEntryResult; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface UserDashboardPreferenceConvert { + + UserDashboardPreferenceConvert CONVERT = Mappers.getMapper(UserDashboardPreferenceConvert.class); + + QuickEntryResult toResult(UserDashboardPreference in); + +} diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/dao/dashboardpreference/UserDashboardPreferenceManager.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/dao/dashboardpreference/UserDashboardPreferenceManager.java new file mode 100644 index 000000000..584d02a57 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/dao/dashboardpreference/UserDashboardPreferenceManager.java @@ -0,0 +1,22 @@ +package cn.daxpay.open.platform.iam.dao.dashboardpreference; + +import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager; +import cn.daxpay.open.platform.iam.entity.dashboardpreference.UserDashboardPreference; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +/// # 用户工作台快捷入口偏好 +/// +@Repository +public class UserDashboardPreferenceManager extends BaseManager { + + /// 按用户 + 终端查询偏好 + public Optional findByUserAndClient(Long userId, String clientCode) { + return lambdaQuery() + .eq(UserDashboardPreference::getUserId, userId) + .eq(UserDashboardPreference::getClientCode, clientCode) + .oneOpt(); + } + +} diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/dao/dashboardpreference/UserDashboardPreferenceMapper.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/dao/dashboardpreference/UserDashboardPreferenceMapper.java new file mode 100644 index 000000000..8ee341e9c --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/dao/dashboardpreference/UserDashboardPreferenceMapper.java @@ -0,0 +1,12 @@ +package cn.daxpay.open.platform.iam.dao.dashboardpreference; + +import cn.daxpay.open.platform.iam.entity.dashboardpreference.UserDashboardPreference; +import com.github.yulichang.base.MPJBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/// # 用户工作台快捷入口偏好 +/// +@Mapper +public interface UserDashboardPreferenceMapper extends MPJBaseMapper { + +} diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/entity/dashboardpreference/UserDashboardPreference.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/entity/dashboardpreference/UserDashboardPreference.java new file mode 100644 index 000000000..34bee246e --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/entity/dashboardpreference/UserDashboardPreference.java @@ -0,0 +1,50 @@ +package cn.daxpay.open.platform.iam.entity.dashboardpreference; + +import cn.daxpay.open.platform.common.mybatisplus.base.MpBaseEntity; +import cn.daxpay.open.platform.common.mybatisplus.function.ToResult; +import cn.daxpay.open.platform.common.mybatisplus.handler.type.StringListTypeHandler; +import cn.daxpay.open.platform.iam.convert.dashboardpreference.UserDashboardPreferenceConvert; +import cn.daxpay.open.platform.iam.result.dashboardpreference.QuickEntryResult; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.List; + +/// # 用户工作台快捷入口偏好 +/// +/// 按用户 + 终端(clientCode)维度存储用户在工作台自定义的快捷入口序列. +/// 仅存已选入口的 key 有序列表, 入口元信息(图标/标题/路由)由前端各端自行维护, 后端不感知. +/// PC 与移动端通过 clientCode 区分, 互不影响. +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@TableName(value = "iam_user_dashboard_preference", autoResultMap = true) +public class UserDashboardPreference extends MpBaseEntity implements ToResult { + + /// 用户ID + private Long userId; + + /// 终端编码(WEB / MOBILE), PC 与移动分开管理 + private String clientCode; + + /// 已选快捷入口有序序列(纯 key 数组), 如 ["merchant","notify","app"] + @TableField(typeHandler = StringListTypeHandler.class) + private List entries; + + @Override + public QuickEntryResult toResult() { + return UserDashboardPreferenceConvert.CONVERT.toResult(this); + } + + /// 构造新实例(新增用) + public static UserDashboardPreference init(Long userId, String clientCode, List entries) { + return new UserDashboardPreference() + .setUserId(userId) + .setClientCode(clientCode) + .setEntries(entries); + } + +} diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/param/dashboardpreference/QuickEntrySaveParam.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/param/dashboardpreference/QuickEntrySaveParam.java new file mode 100644 index 000000000..cc58dbfde --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/param/dashboardpreference/QuickEntrySaveParam.java @@ -0,0 +1,18 @@ +package cn.daxpay.open.platform.iam.param.dashboardpreference; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@Accessors(chain = true) +@Schema(title = "工作台快捷入口保存参数") +public class QuickEntrySaveParam { + + /// 已选快捷入口有序序列(纯 key 数组), 允许空数组表示全部隐藏 + @Schema(description = "已选快捷入口有序序列(纯 key 数组), 允许空数组") + private List entries; + +} diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/dashboardpreference/QuickEntryResult.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/dashboardpreference/QuickEntryResult.java new file mode 100644 index 000000000..79cbe4de0 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/dashboardpreference/QuickEntryResult.java @@ -0,0 +1,18 @@ +package cn.daxpay.open.platform.iam.result.dashboardpreference; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@Accessors(chain = true) +@Schema(title = "工作台快捷入口偏好") +public class QuickEntryResult { + + /// 已选快捷入口有序序列(纯 key 数组); 为 null 表示用户尚未自定义, 前端使用默认序列 + @Schema(description = "已选快捷入口有序序列(纯 key 数组), null 表示未自定义") + private List entries; + +} diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/dashboardpreference/UserDashboardPreferenceService.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/dashboardpreference/UserDashboardPreferenceService.java new file mode 100644 index 000000000..5c2ce111c --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/dashboardpreference/UserDashboardPreferenceService.java @@ -0,0 +1,58 @@ +package cn.daxpay.open.platform.iam.service.dashboardpreference; + +import cn.daxpay.open.platform.capability.auth.util.SecurityUtil; +import cn.daxpay.open.platform.iam.dao.dashboardpreference.UserDashboardPreferenceManager; +import cn.daxpay.open.platform.iam.entity.dashboardpreference.UserDashboardPreference; +import cn.daxpay.open.platform.iam.result.dashboardpreference.QuickEntryResult; +import cn.daxpay.open.platform.iam.service.client.ClientCodeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/// # 用户工作台快捷入口偏好 +/// +/// PC 与移动端通过请求终端(clientCode)区分, 每用户每端一份配置, 互不影响. +/// 仅存储用户已选入口的 key 有序序列, 入口元信息由前端维护. +@Service +@RequiredArgsConstructor +public class UserDashboardPreferenceService { + + private final UserDashboardPreferenceManager userDashboardPreferenceManager; + + private final ClientCodeService clientCodeService; + + /// 查询当前用户在当前终端的快捷入口序列 + /// 未自定义时 entries 返回 null, 前端据此使用默认序列 + public QuickEntryResult findCurrent() { + Long userId = SecurityUtil.getUserId(); + String clientCode = clientCodeService.getClientCode(); + return userDashboardPreferenceManager.findByUserAndClient(userId, clientCode) + .map(UserDashboardPreference::toResult) + .orElseGet(() -> new QuickEntryResult().setEntries(null)); + } + + /// 保存(整体覆盖)当前用户在当前终端的快捷入口序列 + @Transactional(rollbackFor = Exception.class) + public void saveCurrent(List entries) { + Long userId = SecurityUtil.getUserId(); + String clientCode = clientCodeService.getClientCode(); + // 入参为 null 时按空序列处理, 表示全部隐藏 + List safeEntries = entries == null ? new ArrayList<>() : entries; + UserDashboardPreference existed = userDashboardPreferenceManager + .findByUserAndClient(userId, clientCode).orElse(null); + if (existed == null) { + // 新增 + userDashboardPreferenceManager.save( + UserDashboardPreference.init(userId, clientCode, safeEntries)); + } + else { + // 整体覆盖更新 + existed.setEntries(safeEntries); + userDashboardPreferenceManager.updateById(existed); + } + } + +}