diff --git a/pjcloud-integrated/pjcloud-integrated-biz/API-INTEGRATION-GUIDE.md b/pjcloud-integrated/pjcloud-integrated-biz/API-INTEGRATION-GUIDE.md
new file mode 100644
index 000000000..002fb9072
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/API-INTEGRATION-GUIDE.md
@@ -0,0 +1,387 @@
+# 外部系统滚动通知接口集成指南
+
+## 📋 概述
+
+本文档介绍如何集成使用 PJCloud 系统的滚动通知接口,该接口提供安全的 API 访问,供外部系统获取滚动通知信息。
+
+## 🔐 认证方式
+
+接口采用 **AppId + AppSecret** 的认证方式,通过 HTTP 请求头进行身份验证。
+
+### 认证参数
+
+| 参数名 | 位置 | 必填 | 说明 |
+|--------|------|------|------|
+| X-App-Id | 请求头 | 是 | 应用ID,由系统管理员分配 |
+| X-App-Secret | 请求头 | 是 | 应用密钥,由系统管理员分配 |
+
+## 🌐 接口信息
+
+### 滚动通知接口
+
+- **接口地址**: `GET /notice/external/scroll`
+- **请求方式**: GET
+- **认证方式**: 请求头认证
+- **数据格式**: JSON
+
+### 请求参数
+
+#### 查询参数
+| 参数名 | 必填 | 默认值 | 说明 |
+|--------|------|--------|------|
+| limit | 否 | 20 | 返回条数,最大50条 |
+
+#### 请求头示例
+```http
+X-App-Id: 202501270001
+X-App-Secret: 550e8400-e29b-41d4-a716-446655440001
+```
+
+## 📡 响应格式
+
+### 成功响应
+```json
+{
+ "code": 0,
+ "msg": "操作成功",
+ "data": [
+ {
+ "title": "系统维护通知",
+ "createTime": "2025-01-27 10:00:00",
+ "description": "系统将于今晚进行维护升级"
+ },
+ {
+ "title": "新功能上线",
+ "createTime": "2025-01-26 15:30:00",
+ "description": "新增用户管理功能模块"
+ }
+ ]
+}
+```
+
+### 错误响应
+```json
+{
+ "code": 500,
+ "msg": "无效的应用ID或密钥",
+ "data": null
+}
+```
+
+## 💻 集成示例
+
+### JavaScript 调用示例
+
+#### 基础调用
+```javascript
+// 获取滚动通知
+async function getScrollNotices(limit = 20) {
+ try {
+ const response = await fetch('/notice/external/scroll?limit=' + limit, {
+ method: 'GET',
+ headers: {
+ 'X-App-Id': '202501270001',
+ 'X-App-Secret': '550e8400-e29b-41d4-a716-446655440001',
+ 'Content-Type': 'application/json'
+ }
+ });
+
+ const result = await response.json();
+
+ if (result.code === 0) {
+ return result.data;
+ } else {
+ throw new Error(result.msg);
+ }
+ } catch (error) {
+ console.error('获取通知失败:', error);
+ throw error;
+ }
+}
+
+// 使用示例
+getScrollNotices(15)
+ .then(notices => {
+ console.log('获取到通知:', notices);
+ // 处理通知数据
+ displayNotices(notices);
+ })
+ .catch(error => {
+ console.error('错误:', error.message);
+ });
+```
+
+#### 滚动展示组件
+```javascript
+// 滚动通知展示组件
+class ScrollNoticeDisplay {
+ constructor(containerId, appId, appSecret) {
+ this.container = document.getElementById(containerId);
+ this.appId = appId;
+ this.appSecret = appSecret;
+ this.notices = [];
+ this.currentIndex = 0;
+ this.interval = null;
+ }
+
+ // 获取通知数据
+ async fetchNotices() {
+ try {
+ const response = await fetch('/notice/external/scroll?limit=10', {
+ headers: {
+ 'X-App-Id': this.appId,
+ 'X-App-Secret': this.appSecret
+ }
+ });
+
+ const result = await response.json();
+ if (result.code === 0) {
+ this.notices = result.data;
+ this.startScroll();
+ }
+ } catch (error) {
+ console.error('获取通知失败:', error);
+ }
+ }
+
+ // 开始滚动
+ startScroll() {
+ if (this.notices.length === 0) return;
+
+ this.interval = setInterval(() => {
+ this.showNextNotice();
+ }, 3000); // 每3秒切换一次
+
+ this.showNextNotice(); // 立即显示第一条
+ }
+
+ // 显示下一条通知
+ showNextNotice() {
+ if (this.notices.length === 0) return;
+
+ const notice = this.notices[this.currentIndex];
+ this.container.innerHTML = `
+
+ ${notice.title}
+ ${notice.createTime}
+
+ `;
+
+ this.currentIndex = (this.currentIndex + 1) % this.notices.length;
+ }
+
+ // 停止滚动
+ stopScroll() {
+ if (this.interval) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ }
+}
+
+// 使用示例
+const noticeDisplay = new ScrollNoticeDisplay(
+ 'notice-container',
+ '202501270001',
+ '550e8400-e29b-41d4-a716-446655440001'
+);
+
+noticeDisplay.fetchNotices();
+```
+
+### cURL 调用示例
+
+#### 基础调用
+```bash
+curl -X GET "http://your-domain/notice/external/scroll?limit=15" \
+ -H "X-App-Id: 202501270001" \
+ -H "X-App-Secret: 550e8400-e29b-41d4-a716-446655440001"
+```
+
+#### 获取最大条数
+```bash
+curl -X GET "http://your-domain/notice/external/scroll?limit=50" \
+ -H "X-App-Id: 202501270001" \
+ -H "X-App-Secret: 550e8400-e29b-41d4-a716-446655440001"
+```
+
+### Java 调用示例
+
+```java
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+public class NoticeApiClient {
+
+ private final String baseUrl;
+ private final String appId;
+ private final String appSecret;
+ private final RestTemplate restTemplate;
+
+ public NoticeApiClient(String baseUrl, String appId, String appSecret) {
+ this.baseUrl = baseUrl;
+ this.appId = appId;
+ this.appSecret = appSecret;
+ this.restTemplate = new RestTemplate();
+ }
+
+ public List getScrollNotices(int limit) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.set("X-App-Id", appId);
+ headers.set("X-App-Secret", appSecret);
+
+ HttpEntity entity = new HttpEntity<>(headers);
+
+ String url = baseUrl + "/notice/external/scroll?limit=" + limit;
+
+ ResponseEntity response = restTemplate.exchange(
+ url,
+ HttpMethod.GET,
+ entity,
+ ApiResponse.class
+ );
+
+ if (response.getBody() != null && response.getBody().getCode() == 0) {
+ return response.getBody().getData();
+ }
+
+ throw new RuntimeException("获取通知失败");
+ }
+}
+
+// 使用示例
+NoticeApiClient client = new NoticeApiClient(
+ "http://your-domain",
+ "202501270001",
+ "550e8400-e29b-41d4-a716-446655440001"
+);
+
+List notices = client.getScrollNotices(20);
+```
+
+## 🎨 CSS 样式示例
+
+```css
+/* 滚动通知容器 */
+.notice-container {
+ width: 100%;
+ height: 40px;
+ background: linear-gradient(90deg, #f8f9fa, #e9ecef);
+ border: 1px solid #dee2e6;
+ border-radius: 4px;
+ overflow: hidden;
+ position: relative;
+}
+
+/* 通知项 */
+.notice-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 16px;
+ height: 100%;
+ animation: slideIn 0.5s ease-in-out;
+}
+
+/* 通知标题 */
+.notice-title {
+ font-weight: 500;
+ color: #495057;
+ flex: 1;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+/* 通知时间 */
+.notice-time {
+ font-size: 12px;
+ color: #6c757d;
+ margin-left: 16px;
+}
+
+/* 滑动动画 */
+@keyframes slideIn {
+ from {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ .notice-container {
+ height: 50px;
+ }
+
+ .notice-item {
+ padding: 12px 16px;
+ }
+
+ .notice-time {
+ display: none; /* 移动端隐藏时间 */
+ }
+}
+```
+
+## ⚠️ 注意事项
+
+### 安全要求
+1. **密钥保护**: 请妥善保管 AppSecret,不要泄露给第三方
+2. **HTTPS 传输**: 建议使用 HTTPS 协议进行数据传输
+3. **访问频率**: 建议控制调用频率,避免频繁请求
+
+### 技术限制
+1. **返回条数**: 单次请求最多返回 50 条通知
+2. **数据时效**: 只返回已发布的公告信息
+3. **排序规则**: 按置顶状态和创建时间倒序排列
+
+### 错误处理
+1. **认证失败**: 检查 AppId 和 AppSecret 是否正确
+2. **网络异常**: 实现重试机制和超时处理
+3. **数据异常**: 对返回数据进行有效性验证
+
+## 🔧 故障排除
+
+### 常见问题
+
+#### 1. 认证失败
+**错误信息**: "无效的应用ID或密钥"
+**解决方案**:
+- 检查 AppId 和 AppSecret 是否正确
+- 确认应用是否已启用
+- 联系系统管理员验证配置
+
+#### 2. 网络超时
+**错误信息**: 请求超时或无响应
+**解决方案**:
+- 检查网络连接
+- 确认服务器地址是否正确
+- 实现请求超时和重试机制
+
+#### 3. 数据为空
+**可能原因**:
+- 当前没有已发布的公告
+- 查询参数设置不当
+- 系统维护中
+
+## 📞 技术支持
+
+如果在集成过程中遇到问题,请联系技术支持:
+
+- **技术文档**: 本文档
+- **API 地址**: `/notice/external/scroll`
+- **认证方式**: AppId + AppSecret
+- **数据格式**: JSON
+
+---
+
+*最后更新时间: 2025-01-27*
+*版本: v1.0*
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/README-EXTERNAL-API.md b/pjcloud-integrated/pjcloud-integrated-biz/README-EXTERNAL-API.md
new file mode 100644
index 000000000..039105fe3
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/README-EXTERNAL-API.md
@@ -0,0 +1,98 @@
+# 外部系统滚动通知接口使用说明
+
+## 接口概述
+
+提供安全的API访问,供其他系统获取滚动通知信息。
+
+## 接口信息
+
+- **接口地址**: `GET /notice/external/scroll`
+- **请求方式**: GET
+- **认证方式**: 请求头认证
+
+## 请求参数
+
+### 请求头
+| 参数名 | 必填 | 说明 |
+|--------|------|------|
+| X-App-Id | 是 | 应用ID,在配置文件中配置 |
+| X-App-Secret | 是 | 应用密钥,在配置文件中配置 |
+
+### 查询参数
+| 参数名 | 必填 | 默认值 | 说明 |
+|--------|------|--------|------|
+| limit | 否 | 20 | 返回条数,最大50条 |
+
+## 响应格式
+
+```json
+{
+ "code": 0,
+ "msg": "操作成功",
+ "data": [
+ {
+ "title": "通知标题",
+ "createTime": "2025-01-27 10:00:00",
+ "description": "通知描述"
+ }
+ ]
+}
+```
+
+## 使用示例
+
+### JavaScript 调用示例
+```javascript
+fetch('/notice/external/scroll?limit=15', {
+ method: 'GET',
+ headers: {
+ 'X-App-Id': 'external_app_001',
+ 'X-App-Secret': 'secret_2024_001'
+ }
+})
+.then(response => response.json())
+.then(data => {
+ if (data.code === 0) {
+ // 处理通知数据
+ console.log(data.data);
+ }
+})
+.catch(error => {
+ console.error('获取通知失败:', error);
+});
+```
+
+### cURL 调用示例
+```bash
+curl -X GET "http://your-domain/notice/external/scroll?limit=10" \
+ -H "X-App-Id: external_app_001" \
+ -H "X-App-Secret: secret_2024_001"
+```
+
+## 配置说明
+
+在 `application.yml` 中添加以下配置:
+
+```yaml
+pjcloud:
+ external-app:
+ apps:
+ - appId: "your_app_id"
+ appSecret: "your_app_secret"
+ appName: "你的应用名称"
+ enabled: true
+```
+
+## 安全特性
+
+1. **应用认证**: 通过 appId 和 appSecret 验证调用方身份
+2. **访问控制**: 只返回已发布的公告
+3. **数据限制**: 限制最大返回条数,防止恶意调用
+4. **信息脱敏**: 只返回必要的滚动信息
+
+## 注意事项
+
+1. 请妥善保管应用密钥,不要泄露给第三方
+2. 建议使用HTTPS协议进行传输
+3. 可以根据需要调整返回条数限制
+4. 支持动态启用/禁用应用访问权限
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/config/ExternalAppConfig.java b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/config/ExternalAppConfig.java
new file mode 100644
index 000000000..3016692ff
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/config/ExternalAppConfig.java
@@ -0,0 +1,51 @@
+package com.bsd.pjcloud.integrated.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 外部应用配置
+ * 用于管理外部系统的应用ID和密钥
+ *
+ * @author zjf
+ * @date 2025-01-27
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "pjcloud.external-app")
+public class ExternalAppConfig {
+
+ /**
+ * 外部应用列表
+ */
+ private List apps;
+
+ /**
+ * 应用凭证
+ */
+ @Data
+ public static class AppCredential {
+ /**
+ * 应用ID
+ */
+ private String appId;
+
+ /**
+ * 应用密钥
+ */
+ private String appSecret;
+
+ /**
+ * 应用名称
+ */
+ private String appName;
+
+ /**
+ * 是否启用
+ */
+ private Boolean enabled = true;
+ }
+}
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/CleanUserController.java b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/CleanUserController.java
new file mode 100644
index 000000000..55a264282
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/CleanUserController.java
@@ -0,0 +1,139 @@
+package com.bsd.pjcloud.integrated.controller;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.bsd.pjcloud.common.core.util.R;
+import com.bsd.pjcloud.common.log.annotation.SysLog;
+import com.bsd.pjcloud.common.security.util.SecurityUtils;
+import com.bsd.pjcloud.integrated.entity.CleanUserEntity;
+import com.bsd.pjcloud.integrated.service.CleanUserService;
+import org.springframework.security.access.prepost.PreAuthorize;
+import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.http.HttpHeaders;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 保洁信息
+ *
+ * @author zjf
+ * @date 2025-08-11 11:07:52
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/cleanUser" )
+@Tag(description = "cleanUser" , name = "保洁信息管理" )
+@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
+public class CleanUserController {
+
+ private final CleanUserService cleanUserService;
+
+ /**
+ * 分页查询
+ * @param page 分页对象
+ * @param cleanUser 保洁信息
+ * @return
+ */
+ @Operation(summary = "分页查询" , description = "分页查询" )
+ @GetMapping("/page" )
+ @PreAuthorize("@pms.hasPermission('integrated_cleanUser_view')" )
+ public R getCleanUserPage(@ParameterObject Page page, @ParameterObject CleanUserEntity cleanUser) {
+ LambdaQueryWrapper wrapper = Wrappers.lambdaQuery();
+ wrapper.eq(StrUtil.isNotBlank(cleanUser.getName()),CleanUserEntity::getName,cleanUser.getName());
+ wrapper.eq(StrUtil.isNotBlank(cleanUser.getPhone()),CleanUserEntity::getPhone,cleanUser.getPhone());
+ return R.ok(cleanUserService.page(page, wrapper));
+ }
+
+
+ /**
+ * 通过id查询保洁信息
+ * @param id id
+ * @return R
+ */
+ @Operation(summary = "通过id查询" , description = "通过id查询" )
+ @GetMapping("/{id}" )
+ @PreAuthorize("@pms.hasPermission('integrated_cleanUser_view')" )
+ public R getById(@PathVariable("id" ) Long id) {
+ return R.ok(cleanUserService.getById(id));
+ }
+
+ /**
+ * 新增保洁信息
+ * @param cleanUser 保洁信息
+ * @return R
+ */
+ @Operation(summary = "新增保洁信息" , description = "新增保洁信息" )
+ @SysLog("新增保洁信息" )
+ @PostMapping
+ @PreAuthorize("@pms.hasPermission('integrated_cleanUser_add')" )
+ public R save(@RequestBody CleanUserEntity cleanUser) {
+ cleanUser.setDeptId(SecurityUtils.getUser().getDeptId());
+ return R.ok(cleanUserService.save(cleanUser));
+ }
+
+ /**
+ * 修改保洁信息
+ * @param cleanUser 保洁信息
+ * @return R
+ */
+ @Operation(summary = "修改保洁信息" , description = "修改保洁信息" )
+ @SysLog("修改保洁信息" )
+ @PutMapping
+ @PreAuthorize("@pms.hasPermission('integrated_cleanUser_edit')" )
+ public R updateById(@RequestBody CleanUserEntity cleanUser) {
+ return R.ok(cleanUserService.updateById(cleanUser));
+ }
+
+ /**
+ * 通过id删除保洁信息
+ * @param ids id列表
+ * @return R
+ */
+ @Operation(summary = "通过id删除保洁信息" , description = "通过id删除保洁信息" )
+ @SysLog("通过id删除保洁信息" )
+ @DeleteMapping
+ @PreAuthorize("@pms.hasPermission('integrated_cleanUser_del')" )
+ public R removeById(@RequestBody Long[] ids) {
+ return R.ok(cleanUserService.removeBatchByIds(CollUtil.toList(ids)));
+ }
+
+
+ /**
+ * 导出excel 表格
+ * @param cleanUser 查询条件
+ * @param ids 导出指定ID
+ * @return excel 文件流
+ */
+ @ResponseExcel
+ @GetMapping("/export")
+ @PreAuthorize("@pms.hasPermission('integrated_cleanUser_export')" )
+ public List export(CleanUserEntity cleanUser,Long[] ids) {
+ return cleanUserService.list(Wrappers.lambdaQuery(cleanUser).in(ArrayUtil.isNotEmpty(ids), CleanUserEntity::getId, ids));
+ }
+
+ /**
+ * 获取保洁用户下拉列表
+ * @return R
+ */
+ @Operation(summary = "获取保洁用户下拉列表", description = "获取保洁用户下拉列表")
+ @GetMapping("/dropdown")
+// @PreAuthorize("@pms.hasPermission('integrated_cleanUser_view')")
+ public R getCleanUserDropdown() {
+ LambdaQueryWrapper wrapper = Wrappers.lambdaQuery();
+ wrapper.select(CleanUserEntity::getId, CleanUserEntity::getName, CleanUserEntity::getPhone);
+ wrapper.orderByAsc(CleanUserEntity::getName);
+ List list = cleanUserService.list(wrapper);
+ return R.ok(list);
+ }
+}
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/NoticeController.java b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/NoticeController.java
index 1a12702f3..4c47aae07 100644
--- a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/NoticeController.java
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/NoticeController.java
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.log.annotation.SysLog;
+import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import com.bsd.pjcloud.integrated.entity.NoticeEntity;
import com.bsd.pjcloud.integrated.service.NoticeService;
@@ -22,6 +23,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import com.bsd.pjcloud.integrated.vo.notice.NoticeScreenVo;
import java.util.stream.Collectors;
+import com.bsd.pjcloud.integrated.config.ExternalAppConfig;
import java.util.List;
import java.util.Objects;
@@ -40,6 +42,7 @@ import java.util.Objects;
public class NoticeController {
private final NoticeService noticeService;
+ private final ExternalAppConfig externalAppConfig;
/**
* 分页查询
@@ -192,4 +195,71 @@ public class NoticeController {
}).collect(Collectors.toList());
return R.ok(voList);
}
+
+ /**
+ * 外部系统滚动通知接口
+ * 提供安全的API访问,用于其他系统展示滚动通知
+ * @param appId 应用ID
+ * @param appSecret 应用密钥
+ * @param limit 返回条数限制,默认20条
+ * @return R
+ */
+ @Operation(summary = "外部系统滚动通知接口", description = "提供安全的API访问,用于其他系统展示滚动通知")
+ @GetMapping("/external/scroll")
+ @Inner(value = false)
+ public R getExternalScrollNotices(
+ @RequestHeader("X-App-Id") String appId,
+ @RequestHeader("X-App-Secret") String appSecret,
+ @RequestParam(defaultValue = "20") Integer limit) {
+
+ // 验证应用ID和密钥
+ if (!isValidAppCredentials(appId, appSecret)) {
+ return R.failed("无效的应用ID或密钥");
+ }
+
+ // 限制最大返回条数,防止恶意调用
+ if (limit > 50) {
+ limit = 50;
+ }
+
+ LambdaQueryWrapper wrapper = Wrappers.lambdaQuery();
+ wrapper.eq(NoticeEntity::getStatus, "0"); // 只查询已发布的公告
+ wrapper.orderByDesc(NoticeEntity::getIsTop, NoticeEntity::getCreateTime); // 置顶优先,时间倒序
+ wrapper.last("limit " + limit);
+
+ List list = noticeService.list(wrapper);
+
+ // 转换为简化的VO对象,只返回必要信息
+ List voList = list.stream().map(e -> {
+ NoticeScreenVo vo = new NoticeScreenVo();
+ vo.setTitle(e.getTitle());
+ vo.setCreateTime(e.getCreateTime());
+ vo.setDescription(e.getDescription());
+ return vo;
+ }).collect(Collectors.toList());
+
+ return R.ok(voList);
+ }
+
+ /**
+ * 验证应用ID和密钥
+ * @param appId 应用ID
+ * @param appSecret 应用密钥
+ * @return 是否有效
+ */
+ private boolean isValidAppCredentials(String appId, String appSecret) {
+ // 从配置中读取有效的应用ID和密钥
+ if (StrUtil.isBlank(appId) || StrUtil.isBlank(appSecret)) {
+ return false;
+ }
+
+ // 从配置中验证应用凭证
+ if (externalAppConfig.getApps() != null) {
+ return externalAppConfig.getApps().stream()
+ .filter(ExternalAppConfig.AppCredential::getEnabled)
+ .anyMatch(app -> appId.equals(app.getAppId()) && appSecret.equals(app.getAppSecret()));
+ }
+
+ return false;
+ }
}
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/entity/CleanUserEntity.java b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/entity/CleanUserEntity.java
new file mode 100644
index 000000000..6cc5b95db
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/entity/CleanUserEntity.java
@@ -0,0 +1,97 @@
+package com.bsd.pjcloud.integrated.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import com.bsd.pjcloud.common.core.util.TenantTable;
+import java.time.LocalDateTime;
+
+/**
+ * 保洁信息
+ *
+ * @author zjf
+ * @date 2025-08-11 11:07:52
+ */
+@Data
+@TenantTable
+@TableName("clean_user")
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "保洁信息")
+public class CleanUserEntity extends Model {
+
+
+ /**
+ * id
+ */
+ @TableId(type = IdType.ASSIGN_ID)
+ @Schema(description="id")
+ private Long id;
+
+ /**
+ * 姓名
+ */
+ @Schema(description="姓名")
+ private String name;
+
+ /**
+ * 手机号
+ */
+ @Schema(description="手机号")
+ private String phone;
+
+ /**
+ * 部门id
+ */
+ @Schema(description="部门id")
+ private Long deptId;
+
+ /**
+ * 照片
+ */
+ @Schema(description="照片")
+ private String picUrl;
+
+ /**
+ * 创建者
+ */
+ @TableField(fill = FieldFill.INSERT)
+ @Schema(description="创建者")
+ private String createBy;
+
+ /**
+ * 创建时间
+ */
+ @TableField(fill = FieldFill.INSERT)
+ @Schema(description="创建时间")
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @Schema(description="更新者")
+ private String updateBy;
+
+ /**
+ * 更新时间
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @Schema(description="更新时间")
+ private LocalDateTime updateTime;
+
+ /**
+ * 是否删除
+ */
+ @TableLogic
+ @TableField(fill = FieldFill.INSERT)
+ @Schema(description="是否删除")
+ private String delFlag;
+
+ /**
+ * 租户编号
+ */
+ @Schema(description="租户编号")
+ private Long tenantId;
+}
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/mapper/CleanUserMapper.java b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/mapper/CleanUserMapper.java
new file mode 100644
index 000000000..30e9c0db8
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/mapper/CleanUserMapper.java
@@ -0,0 +1,11 @@
+package com.bsd.pjcloud.integrated.mapper;
+
+import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
+import com.bsd.pjcloud.integrated.entity.CleanUserEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface CleanUserMapper extends PjcloudBaseMapper {
+
+
+}
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/service/CleanUserService.java b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/service/CleanUserService.java
new file mode 100644
index 000000000..1c91dacbf
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/service/CleanUserService.java
@@ -0,0 +1,8 @@
+package com.bsd.pjcloud.integrated.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.bsd.pjcloud.integrated.entity.CleanUserEntity;
+
+public interface CleanUserService extends IService {
+
+}
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/service/impl/CleanUserServiceImpl.java b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/service/impl/CleanUserServiceImpl.java
new file mode 100644
index 000000000..ce687bf0e
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/service/impl/CleanUserServiceImpl.java
@@ -0,0 +1,16 @@
+package com.bsd.pjcloud.integrated.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.bsd.pjcloud.integrated.entity.CleanUserEntity;
+import com.bsd.pjcloud.integrated.mapper.CleanUserMapper;
+import com.bsd.pjcloud.integrated.service.CleanUserService;
+import org.springframework.stereotype.Service;
+/**
+ * 保洁信息
+ *
+ * @author zjf
+ * @date 2025-08-11 11:07:52
+ */
+@Service
+public class CleanUserServiceImpl extends ServiceImpl implements CleanUserService {
+}
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/application-external-app.yml b/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/application-external-app.yml
new file mode 100644
index 000000000..c01fb32a5
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/application-external-app.yml
@@ -0,0 +1,29 @@
+# 外部应用配置示例
+# 将此配置添加到你的 application.yml 或 application-{profile}.yml 文件中
+
+pjcloud:
+ external-app:
+ apps:
+ # 外部系统1
+ - appId: "202501270001"
+ appSecret: "550e8400-e29b-41d4-a716-446655440001"
+ appName: "外部系统A"
+ enabled: true
+
+ # 外部系统2
+ - appId: "202501270002"
+ appSecret: "550e8400-e29b-41d4-a716-446655440002"
+ appName: "外部系统B"
+ enabled: true
+
+ # 外部系统3
+ - appId: "202501270003"
+ appSecret: "550e8400-e29b-41d4-a716-446655440003"
+ appName: "外部系统C"
+ enabled: true
+
+ # 外部系统4(已禁用)
+ - appId: "202501270004"
+ appSecret: "550e8400-e29b-41d4-a716-446655440004"
+ appName: "外部系统D"
+ enabled: false
\ No newline at end of file
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/application.yml b/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/application.yml
index c091aaecc..1fc8795d3 100644
--- a/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/application.yml
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/application.yml
@@ -26,3 +26,11 @@ spring:
visitor:
jwt-secret: "u8Jk2n3Lk4pQ5r6T7s8V9w0X1y2Z3a4B"
qr-secret: "Qw8eR2t9Yp3sV6bN1mZ4xT7cL0vB5nH2" # 新增二维码secret,随机生成
+
+pjcloud:
+ external-app:
+ apps:
+ - appId: "202501270001"
+ appSecret: "550e8400-e29b-41d4-a716-446655440001"
+ appName: "通知公告"
+ enabled: true
diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/mapper/CleanUserMapper.xml b/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/mapper/CleanUserMapper.xml
new file mode 100644
index 000000000..f02641609
--- /dev/null
+++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/resources/mapper/CleanUserMapper.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file