diff --git a/src/main/java/com/yunhai/userbase/common/converter/ClientEnumConverter.java b/src/main/java/com/yunhai/userbase/common/converter/ClientEnumConverter.java
index e7a51c2..1899280 100644
--- a/src/main/java/com/yunhai/userbase/common/converter/ClientEnumConverter.java
+++ b/src/main/java/com/yunhai/userbase/common/converter/ClientEnumConverter.java
@@ -1,6 +1,6 @@
package com.yunhai.userbase.common.converter;
-import com.yunhai.userbase.enums.ClientEnum;
+import com.yunhai.platform.utils.enums.ClientEnum;
import javax.persistence.AttributeConverter;
diff --git a/src/main/java/com/yunhai/userbase/common/converter/CommonIntEnumConverter.java b/src/main/java/com/yunhai/userbase/common/converter/CommonIntEnumConverter.java
index 33c8dc6..47be82a 100644
--- a/src/main/java/com/yunhai/userbase/common/converter/CommonIntEnumConverter.java
+++ b/src/main/java/com/yunhai/userbase/common/converter/CommonIntEnumConverter.java
@@ -1,6 +1,6 @@
package com.yunhai.userbase.common.converter;
-import com.yunhai.userbase.enums.IEnum;
+import com.yunhai.platform.utils.enums.IEnum;
import javax.persistence.AttributeConverter;
diff --git a/src/main/java/com/yunhai/userbase/controller/api/BaseController.java b/src/main/java/com/yunhai/userbase/controller/api/BaseController.java
index 6ff2ea8..ee159c5 100644
--- a/src/main/java/com/yunhai/userbase/controller/api/BaseController.java
+++ b/src/main/java/com/yunhai/userbase/controller/api/BaseController.java
@@ -1,6 +1,8 @@
package com.yunhai.userbase.controller.api;
import com.yunhai.platform.utils.model.BoaUser;
+import com.yunhai.platform.utils.enums.ClientEnum;
+import com.yunhai.platform.utils.enums.IEnum;
import com.yunhai.userbase.config.Config;
import com.yunhai.userbase.enums.*;
import com.yunhai.userbase.utils.*;
diff --git a/src/main/java/com/yunhai/userbase/controller/oapi/AppController.java b/src/main/java/com/yunhai/userbase/controller/oapi/AppController.java
new file mode 100644
index 0000000..37ea302
--- /dev/null
+++ b/src/main/java/com/yunhai/userbase/controller/oapi/AppController.java
@@ -0,0 +1,45 @@
+package com.yunhai.userbase.controller.oapi;
+
+import com.yunhai.platform.utils.enums.ClientEnum;
+import com.yunhai.platform.utils.enums.IEnum;
+import com.yunhai.platform.utils.model.BoaAppType;
+import com.yunhai.userbase.config.Config;
+import com.yunhai.userbase.service.IAppCategoryService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 应用域开放接口:供其他业务服务经 yh-app-auth 签名调用({@code /oapi/app/...})。
+ * 接口本身不做验签——签名由 {@code OpenApiAuthEnforceConfiguration} 在过滤器层强制要求
+ * (URL pattern {@code /oapi/*} 已覆盖本控制器),未带 {@code X-App-Key/X-Timestamp/X-Signature} 的请求直接 401。
+ */
+@RestController
+@RequestMapping(Config.OPEN_API_PATH + "app/")
+public class AppController {
+
+ @Autowired
+ private IAppCategoryService appCategoryService;
+
+ /**
+ * 查询用户在指定客户端类型下有权访问的应用列表(按应用类型分组)。
+ * 权限过滤复用 {@code IGroupAuthzService.findUserAuthorizedAppIds}(组开通 ∪ 启用角色模块所属应用),
+ * 超级管理员返回全部;不透出 appSecret。
+ * clientType 传枚举 key(2 的幂值,如 1=PC 2=移动App 4=系统模块…);非法/缺失按不限制端处理。
+ *
+ * @param userId 用户ID(必填)
+ * @param clientType 客户端类型 key(可选)
+ * @return 应用类型分组列表;无权限时各类型 apps 为空数组
+ */
+ @GetMapping("list")
+ public List list(@RequestParam("userId") Integer userId,
+ @RequestParam(value = "clientType", required = false) Integer clientType) {
+ ClientEnum clientEnum = (clientType == null) ? null
+ : IEnum.getByKey(ClientEnum.class, clientType);
+ return appCategoryService.listBoaAppType(userId, clientEnum);
+ }
+}
diff --git a/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java b/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java
index c34b6ba..e070276 100644
--- a/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java
+++ b/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java
@@ -4,12 +4,14 @@ import com.yunhai.platform.appauth.AppAuthFilter;
import com.yunhai.platform.utils.exception.DException;
import com.yunhai.platform.utils.model.BoaGroup;
import com.yunhai.platform.utils.model.BoaModule;
+import com.yunhai.platform.utils.model.BoaRoleInfo;
import com.yunhai.platform.utils.model.BoaUser;
import com.yunhai.userbase.common.validation.ValidationGroups;
import com.yunhai.userbase.config.Config;
import com.yunhai.userbase.entity.ApplicationInfo;
import com.yunhai.userbase.model.sysuser.SysUserGroupDeleteItem;
import com.yunhai.userbase.model.sysuser.SysUserMoveRequest;
+import com.yunhai.userbase.model.sysuser.SysUserPasswordRequest;
import com.yunhai.userbase.model.sysuser.SysUserRequest;
import com.yunhai.userbase.repository.ApplicationInfoRepository;
import com.yunhai.userbase.service.IGroupAuthzService;
@@ -209,4 +211,22 @@ public class UserController {
sysUserService.moveUserWithinSchool(request.getUserId(), request.getGroupId(), request.getSchoolId());
return userCenterService.getUserInfoWithDeptById(request.getUserId());
}
+
+ /**
+ * 查询用户的业务角色身份列表(角色 + 主归属学校组上下文)。
+ * 鉴权由 OpenApiAuthEnforceConfiguration 在过滤器层强制签名。
+ */
+ @GetMapping("roles")
+ public List roles(@RequestParam("userId") Integer userId) {
+ return sysUserService.findUserRoles(userId);
+ }
+
+ /**
+ * 修改用户密码:先校验旧密码,再写入新密码(BCrypt)。
+ * 鉴权由 OpenApiAuthEnforceConfiguration 在过滤器层强制签名;此处不记操作日志(避免明文密码写入 request_params)。
+ */
+ @PostMapping("password")
+ public void password(@RequestBody @Validated SysUserPasswordRequest request) {
+ sysUserService.changePassword(request.getUserId(), request.getOldPassword(), request.getNewPassword());
+ }
}
diff --git a/src/main/java/com/yunhai/userbase/enums/ClientEnum.java b/src/main/java/com/yunhai/userbase/enums/ClientEnum.java
deleted file mode 100644
index 1451dc8..0000000
--- a/src/main/java/com/yunhai/userbase/enums/ClientEnum.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.yunhai.userbase.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * @author chenx
- * @date 2026/07/13
- * @description 客户端来源
- */
-@Getter
-@AllArgsConstructor
-public enum ClientEnum implements IEnum {
-
- PC_BROWSER(1, "Pc浏览器"),
- MOBILE_APP(2, "移动App"),
- SYSTEM_MODULE(4, "系统模块"),
- WECHAT_OFFICIAL(8, "微信公众号"),
- WORK_WECHAT(16, "企业微信"),
- DING_TALK(32, "钉钉"),
- STUDENT_DEVICE(64, "学伴机");
-
- private Integer key;
- private String value;
-
- @Override
- public Integer getKey() {
- return this.key;
- }
-
- @Override
- public String getValue() {
- return this.value;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/yunhai/userbase/enums/GenderEnum.java b/src/main/java/com/yunhai/userbase/enums/GenderEnum.java
index 9650178..f456f66 100644
--- a/src/main/java/com/yunhai/userbase/enums/GenderEnum.java
+++ b/src/main/java/com/yunhai/userbase/enums/GenderEnum.java
@@ -1,5 +1,7 @@
package com.yunhai.userbase.enums;
+import com.yunhai.platform.utils.enums.IEnum;
+
import lombok.AllArgsConstructor;
import lombok.Getter;
diff --git a/src/main/java/com/yunhai/userbase/enums/GroupTypeEnum.java b/src/main/java/com/yunhai/userbase/enums/GroupTypeEnum.java
index 19c3c55..2c7fd11 100644
--- a/src/main/java/com/yunhai/userbase/enums/GroupTypeEnum.java
+++ b/src/main/java/com/yunhai/userbase/enums/GroupTypeEnum.java
@@ -1,5 +1,7 @@
package com.yunhai.userbase.enums;
+import com.yunhai.platform.utils.enums.IEnum;
+
import lombok.AllArgsConstructor;
import lombok.Getter;
diff --git a/src/main/java/com/yunhai/userbase/enums/IEnum.java b/src/main/java/com/yunhai/userbase/enums/IEnum.java
deleted file mode 100644
index 491f759..0000000
--- a/src/main/java/com/yunhai/userbase/enums/IEnum.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.yunhai.userbase.enums;
-
-import com.yunhai.userbase.utils.EnumUtils;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 统一枚举规范接口,仅定义契约getKey/getValue
- * 内置静态工具方法,业务直接 IEnum.xxx(枚举类) 调用
- * @param key类型
- */
-public interface IEnum {
-
- /** 获取编码key */
- T getKey();
-
- /** 获取中文展示value */
- String getValue();
-
- // ====================== 静态工具入口(统一对外调用) ======================
- /** 获取枚举全部实例列表 */
- static & IEnum>> List toList(Class clazz) {
- return EnumUtils.toList(clazz);
- }
-
- /** 根据key获取枚举,无匹配返回null */
- static & IEnum>> E getByKey(Class clazz, Object key) {
- return EnumUtils.getEnumByKey(clazz, key);
- }
-
- /** 根据key严格获取,不存在抛异常(对应fromKey) */
- static & IEnum>> E fromKey(Class clazz, Object key) {
- return EnumUtils.fromKey(clazz, key);
- }
-
- /** 根据key获取中文value,无匹配返回空串 */
- static & IEnum>> String getValueByKey(Class clazz, Object key) {
- return EnumUtils.getValue(clazz, key);
- }
-
- /** 根据中文value获取key字符串,无匹配返回空串 */
- static & IEnum>> String getKeyByValue(Class clazz, String value) {
- return EnumUtils.getKey(clazz, value);
- }
-
- /** 判断key是否存在 */
- static & IEnum>> boolean existsKey(Class clazz, Object key) {
- return EnumUtils.isExistKey(clazz, key);
- }
-
- /** 判断value是否存在 */
- static & IEnum>> boolean existsValue(Class clazz, String value) {
- return EnumUtils.isExistValue(clazz, value);
- }
-
- /** 转完整Map列表(包含enum实例、name、key、value) */
- static & IEnum>> List