diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..10b731c
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..639900d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..20c0107
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/urbanLifeline.iml b/.idea/urbanLifeline.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/urbanLifeline.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..a9ec60e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "maven.view": "hierarchical"
+}
\ No newline at end of file
diff --git a/docs/数据库表结构速查.md b/docs/数据库表结构速查.md
new file mode 100644
index 0000000..7e979be
--- /dev/null
+++ b/docs/数据库表结构速查.md
@@ -0,0 +1,599 @@
+# 数据库表结构速查手册
+
+## 快速导航
+
+- [系统基础模块 (sys)](#系统基础模块-sys)
+- [文件管理模块 (file)](#文件管理模块-file)
+- [消息通知模块 (message)](#消息通知模块-message)
+- [日志模块 (log)](#日志模块-log)
+- [配置管理模块 (config)](#配置管理模块-config)
+- [知识库管理模块 (knowledge)](#知识库管理模块-knowledge)
+- [招投标智能体模块 (bidding)](#招投标智能体模块-bidding)
+- [智能客服系统模块 (customer_service)](#智能客服系统模块-customerservice)
+- [智能体管理模块 (agent)](#智能体管理模块-agent)
+
+---
+
+## 系统基础模块 (sys)
+
+### 核心表结构
+
+| 表名 | 说明 | 主键 | 核心字段 |
+|------|------|------|----------|
+| `tb_sys_user` | 用户表 | user_id | email, phone, wechat_id, status |
+| `tb_sys_user_info` | 用户信息表 | user_id | avatar, full_name, gender, level |
+| `tb_sys_dept` | 部门表 | dept_id | name, parent_id, dept_path |
+| `tb_sys_role` | 角色表 | role_id | name, scope, owner_dept_id |
+| `tb_sys_permission` | 权限表 | permission_id | name, code, module_id |
+| `tb_sys_user_role` | 用户角色关联表 | (user_id, role_id) | - |
+| `tb_sys_role_permission` | 角色权限关联表 | (role_id, permission_id) | - |
+| `tb_sys_view` | 视图/菜单表 | view_id | name, url, component, type |
+| `tb_sys_module` | 模块表 | module_id | name, description |
+| `tb_sys_acl` | 对象级权限表 | acl_id | object_type, object_id, principal_type, permission |
+| `tb_sys_acl_policy` | ACL策略表 | policy_id | object_type, edit_hierarchy_rule, view_hierarchy_rule |
+| `tb_sys_login_log` | 登录日志表 | optsn | user_id, ip_address, login_time, status |
+| `tb_sys_user_dept` | 用户部门关联表 | (user_id, dept_id) | is_primary, position |
+
+### 权限模型
+
+```
+用户 (tb_sys_user)
+ ├─ 用户角色 (tb_sys_user_role)
+ │ └─ 角色 (tb_sys_role)
+ │ ├─ 角色权限 (tb_sys_role_permission)
+ │ │ └─ 权限 (tb_sys_permission)
+ │ │ └─ 模块 (tb_sys_module)
+ │ └─ 视图权限 (tb_sys_view_permission)
+ │ └─ 视图 (tb_sys_view)
+ └─ 对象权限 (tb_sys_acl)
+ └─ ACL策略 (tb_sys_acl_policy)
+```
+
+### 常用查询
+
+```sql
+-- 查询用户所有权限
+SELECT p.code, p.name
+FROM sys.tb_sys_user_role ur
+JOIN sys.tb_sys_role_permission rp ON ur.role_id = rp.role_id
+JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
+WHERE ur.user_id = 'USER_ID' AND ur.deleted = false;
+
+-- 查询部门树
+WITH RECURSIVE dept_tree AS (
+ SELECT *, 1 AS level FROM sys.tb_sys_dept WHERE parent_id IS NULL
+ UNION ALL
+ SELECT d.*, dt.level + 1
+ FROM sys.tb_sys_dept d
+ JOIN dept_tree dt ON d.parent_id = dt.dept_id
+)
+SELECT * FROM dept_tree ORDER BY level, dept_id;
+```
+
+---
+
+## 文件管理模块 (file)
+
+### 核心表结构
+
+| 表名 | 说明 | 主键 | 核心字段 |
+|------|------|------|----------|
+| `tb_sys_file` | 文件表 | file_id | name, path, size, type, storage_type, url |
+| `tb_file_relation` | 文件关联表 | relation_id | file_id, object_type, object_id, relation_type |
+
+### 文件类型
+
+- `storage_type`: local(本地存储) / oss(对象存储) / ftp / sftp
+- `relation_type`: attachment(附件) / avatar(头像) / banner(横幅)
+
+### 常用查询
+
+```sql
+-- 查询对象的所有附件
+SELECT f.*
+FROM file.tb_sys_file f
+JOIN file.tb_file_relation fr ON f.file_id = fr.file_id
+WHERE fr.object_type = 'bidding_project'
+ AND fr.object_id = 'PROJECT_ID'
+ AND fr.deleted = false;
+```
+
+---
+
+## 消息通知模块 (message)
+
+### 核心表结构
+
+| 表名 | 说明 | 主键 | 核心字段 |
+|------|------|------|----------|
+| `tb_message` | 消息表 | message_id | title, content, type, status |
+| `tb_message_range` | 消息发送范围表 | optsn | message_id, target_type, target_id, channel |
+| `tb_message_receiver` | 用户消息接收表 | optsn | message_id, user_id, status, read_time |
+| `tb_message_channel` | 消息渠道配置表 | channel_id | channel_code, channel_name, status |
+| `tb_message_template` | 消息模板表 | template_id | template_code, title_template, content_template |
+
+### 消息发送流程
+
+```
+创建消息 (tb_message)
+ ↓
+定义发送范围 (tb_message_range)
+ ├─ target_type: user(指定用户) / dept(部门) / role(角色) / all(全员)
+ ├─ channel: app / sms / email / wechat
+ ↓
+生成接收记录 (tb_message_receiver)
+ └─ status: unread → read → handled / deleted
+```
+
+### 常用查询
+
+```sql
+-- 查询用户未读消息
+SELECT m.*, mr.create_time AS receive_time
+FROM message.tb_message m
+JOIN message.tb_message_receiver mr ON m.message_id = mr.message_id
+WHERE mr.user_id = 'USER_ID'
+ AND mr.status = 'unread'
+ AND mr.deleted = false
+ORDER BY mr.create_time DESC;
+```
+
+---
+
+## 日志模块 (log)
+
+### 核心表结构
+
+| 表名 | 说明 | 主键 | 核心字段 |
+|------|------|------|----------|
+| `tb_sys_log` | 系统日志表 | log_id | type, level, module, message, data, trace_id |
+
+### 日志级别
+
+- `level`: debug / info / warn / error / fatal
+- `type`: system / audit / security / business / api
+
+### 常用查询
+
+```sql
+-- 查询错误日志
+SELECT * FROM log.tb_sys_log
+WHERE level IN ('error', 'fatal')
+ AND create_time > now() - interval '24 hours'
+ORDER BY create_time DESC;
+
+-- 链路追踪
+SELECT * FROM log.tb_sys_log
+WHERE trace_id = 'TRACE_ID'
+ORDER BY create_time;
+```
+
+---
+
+## 配置管理模块 (config)
+
+### 核心表结构
+
+| 表名 | 说明 | 主键 | 核心字段 |
+|------|------|------|----------|
+| `tb_sys_config` | 系统配置表 | config_id | key, name, value, config_type, render_type |
+
+### 配置类型
+
+- `config_type`: String / Integer / Boolean / Float / Double
+- `render_type`: select / input / textarea / checkbox / radio / switch
+
+### 常用查询
+
+```sql
+-- 按模块查询配置
+SELECT * FROM config.tb_sys_config
+WHERE module_id = 'MODULE_ID' AND deleted = false
+ORDER BY order_num;
+```
+
+---
+
+## 知识库管理模块 (knowledge)
+
+### 核心表结构
+
+| 表名 | 说明 | 主键 | 核心字段 |
+|------|------|------|----------|
+| `tb_knowledge_base` | 知识库表 | kb_id | name, kb_type, access_level, version |
+| `tb_knowledge_document` | 知识文档表 | doc_id | kb_id, title, doc_type, category, embedding_status |
+| `tb_knowledge_chunk` | 文档片段表 | chunk_id | doc_id, kb_id, content, embedding |
+| `tb_knowledge_access_log` | 知识访问日志表 | log_id | kb_id, doc_id, user_id, access_type |
+
+### 知识库类型
+
+- `kb_type`: bidding(招投标) / customer_service(客服) / internal(内部协同)
+- `access_level`: public(公开) / private(私有) / internal(内部)
+- `embedding_status`: pending(待处理) / processing(处理中) / completed(完成) / failed(失败)
+
+### RAG检索流程
+
+```
+用户查询
+ ↓
+向量化查询文本
+ ↓
+在 tb_knowledge_chunk 中进行向量检索 (embedding)
+ ↓
+获取相关文档片段
+ ↓
+关联 tb_knowledge_document 获取完整文档信息
+ ↓
+返回结果 + 记录访问日志
+```
+
+### 常用查询
+
+```sql
+-- 查询知识库文档
+SELECT d.*, kb.name AS kb_name
+FROM knowledge.tb_knowledge_document d
+JOIN knowledge.tb_knowledge_base kb ON d.kb_id = kb.kb_id
+WHERE d.kb_id = 'KB_ID'
+ AND d.embedding_status = 'completed'
+ AND d.deleted = false;
+
+-- 向量检索(需要pgvector扩展)
+-- SELECT chunk_id, content,
+-- 1 - (embedding <=> '[查询向量]'::vector) AS similarity
+-- FROM knowledge.tb_knowledge_chunk
+-- WHERE kb_id = 'KB_ID'
+-- ORDER BY embedding <=> '[查询向量]'::vector
+-- LIMIT 10;
+```
+
+---
+
+## 招投标智能体模块 (bidding)
+
+### 核心表结构
+
+| 表名 | 说明 | 主键 | 核心字段 |
+|------|------|------|----------|
+| `tb_bidding_project` | 招标项目表 | project_id | project_no, project_name, project_status, deadline |
+| `tb_bidding_document` | 招标文件表 | doc_id | project_id, doc_type, parse_status, parse_result |
+| `tb_bidding_requirement` | 要素提取表 | req_id | project_id, req_category, is_veto, compliance_status |
+| `tb_bid_response` | 投标文件生成表 | response_id | project_id, response_type, generation_status |
+| `tb_bidding_scoring_rule` | 评分规则表 | rule_id | project_id, rule_category, max_score, our_score |
+| `tb_bidding_process` | 流程节点表 | process_id | project_id, node_type, node_status |
+| `tb_bid_template` | 投标模板表 | template_id | template_name, template_type, usage_count |
+
+### 项目生命周期
+
+```
+collecting(收集中)
+ ↓
+analyzing(分析中) - 智能解读招标文件
+ ↓
+preparing(准备投标) - 生成投标文件
+ ↓
+submitted(已提交)
+ ↓
+opened(已开标)
+ ↓
+won(中标) / lost(未中标) / abandoned(放弃)
+```
+
+### 要素类别
+
+- `req_category`:
+ - commercial: 商务要素
+ - technical: 技术参数
+ - veto: 否决项 ⚠️
+ - qualification: 资质要求
+ - delivery: 交付要求
+ - payment: 付款条件
+ - scoring: 评分标准
+
+### 常用查询
+
+```sql
+-- 查询项目的所有否决项
+SELECT * FROM bidding.tb_bidding_requirement
+WHERE project_id = 'PROJECT_ID'
+ AND is_veto = true
+ AND deleted = false;
+
+-- 查询项目评分预估
+SELECT
+ rule_category,
+ SUM(max_score) AS total_max_score,
+ SUM(our_score) AS total_our_score,
+ ROUND(SUM(our_score) / SUM(max_score) * 100, 2) AS score_percentage
+FROM bidding.tb_bidding_scoring_rule
+WHERE project_id = 'PROJECT_ID' AND deleted = false
+GROUP BY rule_category;
+```
+
+---
+
+## 智能客服系统模块 (customer_service)
+
+### 核心表结构
+
+| 表名 | 说明 | 主键 | 核心字段 |
+|------|------|------|----------|
+| `tb_customer` | 客户信息表 | customer_id | customer_name, phone, wechat_openid, customer_level |
+| `tb_conversation` | 会话表 | conversation_id | customer_id, conversation_type, agent_id, satisfaction_rating |
+| `tb_conversation_message` | 会话消息表 | message_id | conversation_id, sender_type, content, is_ai_generated |
+| `tb_ticket` | 工单表 | ticket_id | ticket_no, customer_id, ticket_type, ticket_status, sla_deadline |
+| `tb_ticket_log` | 工单处理记录表 | log_id | ticket_id, action_type, action_content |
+| `tb_faq` | FAQ表 | faq_id | category, question, answer, hit_count |
+| `tb_service_evaluation` | 客服评价表 | evaluation_id | customer_id, evaluation_type, rating |
+| `tb_crm_config` | CRM集成配置表 | config_id | crm_system, api_endpoint, sync_enabled |
+
+### 会话流程
+
+```
+客户发起咨询
+ ↓
+创建会话 (tb_conversation)
+ ├─ conversation_type: ai(AI客服) / human(人工) / transfer(转接)
+ ↓
+消息交互 (tb_conversation_message)
+ ├─ sender_type: customer / agent / system
+ ├─ is_ai_generated: true/false
+ ├─ kb_references: [知识库文档ID]
+ ↓
+智能工单生成 (tb_ticket)
+ ├─ ticket_source: ai / manual / system
+ ↓
+满意度评价 (tb_service_evaluation / tb_conversation)
+```
+
+### 工单状态
+
+- `ticket_status`:
+ - pending: 待处理
+ - processing: 处理中
+ - resolved: 已解决
+ - closed: 已关闭
+ - cancelled: 已取消
+
+### 常用查询
+
+```sql
+-- 查询客户的活跃会话
+SELECT * FROM customer_service.tb_conversation
+WHERE customer_id = 'CUSTOMER_ID'
+ AND conversation_status = 'active'
+ AND deleted = false;
+
+-- 查询逾期工单
+SELECT * FROM customer_service.tb_ticket
+WHERE is_overdue = true
+ AND ticket_status IN ('pending', 'processing')
+ AND deleted = false
+ORDER BY sla_deadline;
+
+-- 查询AI回答质量
+SELECT
+ DATE(create_time) AS stat_date,
+ AVG(confidence_score) AS avg_confidence,
+ COUNT(*) FILTER (WHERE confidence_score > 0.8) AS high_confidence_count,
+ COUNT(*) AS total_count
+FROM customer_service.tb_conversation_message
+WHERE is_ai_generated = true
+ AND deleted = false
+GROUP BY DATE(create_time);
+```
+
+---
+
+## 智能体管理模块 (agent)
+
+### 核心表结构
+
+| 表名 | 说明 | 主键 | 核心字段 |
+|------|------|------|----------|
+| `tb_agent` | 智能体定义表 | agent_id | agent_code, agent_name, agent_type, model_config |
+| `tb_agent_session` | 智能体会话表 | session_id | agent_id, user_id, session_status, token_usage |
+| `tb_agent_message` | 智能体消息表 | message_id | session_id, role, content, function_call |
+| `tb_agent_tool` | 智能体工具表 | tool_id | tool_code, tool_type, function_schema, api_endpoint |
+| `tb_api_integration` | API集成表 | integration_id | integration_name, base_url, auth_config |
+| `tb_api_call_log` | API调用日志表 | log_id | integration_id, endpoint, response_status, duration_ms |
+| `tb_agent_metrics` | 智能体监控指标表 | metric_id | agent_id, metric_date, total_sessions, total_tokens |
+| `tb_agent_error_log` | 智能体异常日志表 | log_id | agent_id, error_type, error_message, severity |
+| `tb_agent_rating` | 智能体评价表 | rating_id | agent_id, rating, feedback |
+
+### 智能体类型
+
+- `agent_type`:
+ - bidding: 招投标智能体
+ - customer_service: 客服智能体
+ - knowledge_assistant: 知识助手
+ - custom: 自定义智能体
+
+### 工具类型
+
+- `tool_type`:
+ - api: API调用
+ - function: 函数
+ - plugin: 插件
+ - integration: 集成
+
+### 会话流程
+
+```
+用户请求
+ ↓
+创建会话 (tb_agent_session)
+ ↓
+消息交互 (tb_agent_message)
+ ├─ role: user / assistant / system / function
+ ├─ function_call: 工具调用
+ ↓
+工具执行 (tb_agent_tool)
+ ├─ API调用记录 (tb_api_call_log)
+ ↓
+记录监控指标 (tb_agent_metrics)
+ ├─ 错误记录 (tb_agent_error_log)
+ ↓
+用户评价 (tb_agent_rating)
+```
+
+### 常用查询
+
+```sql
+-- 查询智能体使用统计
+SELECT * FROM agent.v_agent_usage_stats
+WHERE agent_id = 'AGENT_ID';
+
+-- 查询API健康状态
+SELECT
+ integration_name,
+ health_status,
+ last_health_check,
+ EXTRACT(EPOCH FROM (now() - last_health_check))/60 AS minutes_since_check
+FROM agent.tb_api_integration
+WHERE deleted = false
+ORDER BY health_status, last_health_check;
+
+-- 查询智能体错误率
+SELECT
+ a.agent_name,
+ COUNT(*) FILTER (WHERE e.severity = 'critical') AS critical_errors,
+ COUNT(*) FILTER (WHERE e.severity = 'error') AS errors,
+ COUNT(*) AS total_errors
+FROM agent.tb_agent a
+LEFT JOIN agent.tb_agent_error_log e ON a.agent_id = e.agent_id
+WHERE e.create_time > now() - interval '24 hours'
+GROUP BY a.agent_id, a.agent_name;
+```
+
+---
+
+## 数据字典常用字段说明
+
+### 通用字段
+
+所有表都包含以下标准字段:
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `optsn` | VARCHAR(50) | 流水号(唯一标识) |
+| `xxx_id` | VARCHAR(50) | 主键ID |
+| `dept_path` | VARCHAR(255) | 部门全路径(多租户隔离) |
+| `creator` | VARCHAR(50) | 创建者ID |
+| `updater` | VARCHAR(50) | 更新者ID |
+| `create_time` | timestamptz | 创建时间(带时区) |
+| `update_time` | timestamptz | 更新时间(触发器自动更新) |
+| `delete_time` | timestamptz | 删除时间 |
+| `deleted` | BOOLEAN | 软删除标记 |
+
+### 时间戳说明
+
+- 使用 `timestamptz` 类型(带时区的时间戳)
+- 自动记录创建时间
+- 更新时间由触发器自动维护
+- 支持软删除(保留 delete_time)
+
+### 部门路径(dept_path)
+
+格式:`/1/2/3/`
+
+- 用于多租户数据隔离
+- 支持 LIKE 递归查询
+- 示例查询:`WHERE dept_path LIKE '/1/2/%'`
+
+---
+
+## 视图速查
+
+| 视图名 | Schema | 说明 |
+|--------|--------|------|
+| `v_user_full_info` | sys | 用户完整信息(含用户信息表) |
+| `v_user_role_permission` | sys | 用户角色权限(含模块) |
+| `v_user_full_permissions` | sys | 用户完整权限(含ACL) |
+| `v_agent_usage_stats` | agent | 智能体使用统计 |
+| `v_agent_realtime_status` | agent | 智能体实时状态 |
+| `v_ticket_stats` | customer_service | 工单统计 |
+| `v_ticket_efficiency` | customer_service | 工单处理效率 |
+| `v_project_stats` | bidding | 招投标项目统计 |
+
+---
+
+## 函数速查
+
+| 函数名 | Schema | 说明 |
+|--------|--------|------|
+| `update_modified_column()` | public | 自动更新update_time触发器函数 |
+| `audit_trigger_func()` | public | 审计日志触发器函数 |
+| `archive_old_logs()` | public | 归档旧日志数据 |
+| `archive_api_logs()` | agent | 归档API调用日志 |
+| `check_table_bloat()` | public | 检查表膨胀情况 |
+| `create_update_triggers()` | public | 批量创建更新触发器 |
+
+---
+
+## 索引策略
+
+### 主要索引类型
+
+1. **B-Tree索引**(默认):主键、外键、常规查询字段
+2. **GIN索引**:JSONB字段、数组字段、全文搜索
+3. **部分索引**:带WHERE条件的索引(如 `WHERE deleted = false`)
+4. **表达式索引**:函数索引(如 `lower(email)`)
+
+### 关键索引示例
+
+```sql
+-- 部分索引(减少索引大小)
+CREATE INDEX idx_xxx ON table_name(column) WHERE deleted = false;
+
+-- GIN索引(数组查询)
+CREATE INDEX idx_xxx ON table_name USING gin(array_column);
+
+-- 全文搜索索引
+CREATE INDEX idx_xxx ON table_name USING gin(text_column gin_trgm_ops);
+
+-- 表达式索引
+CREATE INDEX idx_xxx ON table_name(lower(email));
+```
+
+---
+
+## 性能优化建议
+
+### 查询优化
+
+1. **使用索引**:WHERE、JOIN、ORDER BY字段都要有索引
+2. **避免SELECT ***:只查询需要的字段
+3. **使用LIMIT**:分页查询限制返回行数
+4. **使用EXPLAIN**:分析查询计划
+
+### 批量操作
+
+```sql
+-- 批量插入(使用COPY或批量INSERT)
+COPY table_name FROM '/path/to/file.csv' WITH CSV;
+
+-- 批量更新(使用UPDATE...FROM)
+UPDATE table_name t
+SET column = data.value
+FROM (VALUES (1, 'a'), (2, 'b')) AS data(id, value)
+WHERE t.id = data.id;
+```
+
+### 定期维护
+
+```sql
+-- 分析表(更新统计信息)
+ANALYZE table_name;
+
+-- 清理和分析
+VACUUM ANALYZE table_name;
+
+-- 重建索引
+REINDEX TABLE table_name;
+```
+
+---
+
+**版本**: 1.0
+**更新时间**: 2024-12-02
diff --git a/docs/数据库设计交付总结.md b/docs/数据库设计交付总结.md
new file mode 100644
index 0000000..e89c729
--- /dev/null
+++ b/docs/数据库设计交付总结.md
@@ -0,0 +1,511 @@
+# 泰豪电源AI数智化平台 - 数据库设计交付总结
+
+## 📋 交付概览
+
+基于`功能结构.xml`中定义的系统架构,完成了泰豪电源AI数智化平台的完整数据库设计,覆盖"一个底座、多种智能体"的核心理念和四大业务模块。
+
+---
+
+## 📦 交付内容
+
+### 1. SQL脚本文件
+
+所有文件位于:`urbanLifelineServ\.bin\database\postgres\sql\`
+
+| 文件名 | 说明 | 表数量 |
+|--------|------|--------|
+| `createTablePermission.sql` | 系统权限模块(现有优化) | 11张 |
+| `createTableUser.sql` | 用户管理模块(现有优化) | 3张 |
+| `createTableFile.sql` | 文件管理模块(现有) | 1张 |
+| `createTableMessage.sql` | 消息通知模块(现有) | 4张 |
+| `createTableLog.sql` | 日志模块(现有) | 1张 |
+| `createTableConfig.sql` | 配置管理模块(现有) | 1张 |
+| **`createTableKnowledge.sql`** | ✨ **知识库管理模块(新建)** | **4张** |
+| **`createTableBidding.sql`** | ✨ **招投标智能体业务模块(新建)** | **8张** |
+| **`createTableCustomerService.sql`** | ✨ **智能客服系统业务模块(新建)** | **9张** |
+| **`createTableAgent.sql`** | ✨ **智能体管理模块(新建)** | **11张** |
+| **`createTableAll.sql`** | ✨ **完整初始化脚本(新建)** | - |
+| **`optimizations.sql`** | ✨ **数据库优化补丁(新建)** | - |
+
+**总计:53张核心业务表**
+
+### 2. 文档资料
+
+所有文档位于:`docs\`
+
+| 文件名 | 说明 |
+|--------|------|
+| **`数据库设计文档.md`** | 完整的数据库设计说明文档(23000+字) |
+| **`数据库表结构速查.md`** | 表结构快速参考手册(含常用查询示例) |
+| **`数据库设计交付总结.md`** | 本文档 |
+
+---
+
+## 🎯 核心设计亮点
+
+### 1. 模块化架构设计
+
+采用9个Schema实现业务逻辑隔离:
+
+```
+sys ← 系统基础(用户、权限、部门)
+file ← 文件管理
+message ← 消息通知
+log ← 日志审计
+config ← 系统配置
+knowledge ← 知识库管理(支持RAG检索)
+bidding ← 招投标智能体业务
+customer_service ← 智能客服系统业务
+agent ← 智能体管理和平台基础设施
+```
+
+### 2. 权限体系设计
+
+**RBAC + ACL混合模型**
+
+- **RBAC(基于角色的访问控制)**:
+ - 支持全局角色和部门私有角色
+ - 角色-权限-模块三层结构
+ - 视图/菜单权限独立管理
+
+- **ACL(访问控制列表)**:
+ - 对象级细粒度权限控制
+ - 支持任意对象类型(文章、文件、课程等)
+ - 权限位设计(读/写/执行)
+ - 支持显式拒绝和权限继承
+
+### 3. 多租户支持
+
+- **部门路径隔离**(`dept_path`字段):格式 `/1/2/3/`
+- **行级安全策略**(RLS):基于用户部门自动过滤数据
+- **作用域控制**:角色和权限的作用域管理
+
+### 4. 知识库智能管理
+
+**支持RAG(检索增强生成)流程:**
+
+```
+文档上传 → 智能分类 → AI摘要 → 关键词提取 → 文档切片 → 向量化 → 存储
+ ↓ ↓
+ tb_knowledge_document tb_knowledge_chunk
+ (支持向量检索)
+```
+
+**特性:**
+- 多类型知识库(招投标/客服/内部协同)
+- 版本管理和历史追溯
+- 向量化状态跟踪
+- 访问日志记录
+
+### 5. 招投标全流程管理
+
+**完整业务流程覆盖:**
+
+```
+项目创建 → 文件采集 → 智能解读 → 要素提取 → 评分分析
+ ↓ ↓
+流程跟踪 ← 投标提交 ← 文件审核 ← 投标文件生成 ← 模板管理
+```
+
+**核心功能:**
+- **智能要素提取**:7大类要素(商务/技术/否决项/资质/交付/付款/评分)
+- **AI文件生成**:支持技术标/商务标/综合标
+- **评分规则分析**:自动解析评分标准,预估得分
+- **流程节点管理**:完整的项目生命周期跟踪
+
+### 6. 智能客服系统
+
+**全渠道客服支持:**
+
+```
+微信小程序 → AI问答 → 知识库检索 → 工单生成 → CRM同步
+ ↓ ↓ ↓ ↓
+客户管理 会话记录 工单处理 满意度评价
+```
+
+**特性:**
+- **双终端知识库**:客户咨询知识库(外部)+ 内部资料知识库(内部)
+- **智能会话管理**:AI/人工/转接会话类型,情感分析,意图识别
+- **工单智能处理**:AI自动生成工单,SLA超时预警,CRM双向同步
+- **评价体系**:多维度评价(准确性、速度、友好度等)
+
+### 7. 智能体管理平台
+
+**智能体广场 + 工具集成 + 运维监控:**
+
+```
+智能体定义 → 工具配置 → API集成 → 会话管理 → 监控运维 → 评价反馈
+ ↓ ↓ ↓ ↓ ↓
+ 多模型支持 Function 健康检查 Token统计 错误追踪
+```
+
+**特性:**
+- **智能体广场**:发布/评分/分类/标签管理
+- **工具生态**:支持API调用、函数、插件、集成
+- **监控体系**:实时指标、错误日志、性能分析
+- **成本跟踪**:Token使用量、API调用费用统计
+
+---
+
+## 📊 数据库架构统计
+
+### 表结构统计
+
+| Schema | 表数量 | 主要功能 |
+|--------|--------|----------|
+| sys | 13 | 用户、角色、权限、部门、ACL |
+| file | 2 | 文件管理、文件关联 |
+| message | 5 | 消息、发送范围、接收记录、渠道、模板 |
+| log | 1 | 系统日志 |
+| config | 1 | 系统配置 |
+| knowledge | 4 | 知识库、文档、片段、访问日志 |
+| bidding | 8 | 项目、文件、要素、投标、评分、流程、模板 |
+| customer_service | 9 | 客户、会话、消息、工单、FAQ、评价、CRM |
+| agent | 11 | 智能体、会话、消息、工具、API、监控、错误 |
+| **总计** | **54** | - |
+
+### 索引策略
+
+- **B-Tree索引**:100+ 个(主键、外键、常规查询)
+- **GIN索引**:20+ 个(JSONB、数组、全文搜索)
+- **部分索引**:50+ 个(WHERE deleted = false)
+- **表达式索引**:5+ 个(函数索引)
+
+### 视图和函数
+
+- **业务视图**:8个(权限视图、统计视图)
+- **触发器函数**:2个(自动更新时间、审计日志)
+- **业务函数**:4个(数据归档、性能监控)
+
+---
+
+## 🔧 技术特性
+
+### 1. 数据类型使用
+
+- **timestamptz**:全部时间字段(带时区支持)
+- **JSONB**:配置、元数据、扩展字段
+- **数组(TEXT[])**:标签、关键词、ID列表
+- **DECIMAL**:金额、评分(精确计算)
+- **vector**:向量嵌入(需pgvector扩展,可选)
+
+### 2. 软删除机制
+
+所有业务表支持软删除:
+- `deleted` 字段:BOOLEAN类型,默认false
+- `delete_time` 字段:删除时间戳
+- 索引优化:`WHERE deleted = false`
+
+### 3. 审计追踪
+
+- **通用字段**:creator、updater、create_time、update_time
+- **自动触发器**:update_time自动更新
+- **审计日志**:可选的审计触发器(记录所有变更)
+- **链路追踪**:trace_id、span_id支持分布式追踪
+
+### 4. 性能优化
+
+- **分区表设计**:日志表、指标表建议按时间分区
+- **物化视图**:权限视图可物化提升性能
+- **数据归档**:提供自动归档函数
+- **表膨胀监控**:check_table_bloat()函数
+
+---
+
+## 📈 业务价值
+
+### 1. 支撑功能结构.xml定义的四大业务模块
+
+✅ **资料管理智能化** → knowledge模块
+✅ **招投标自动化** → bidding模块
+✅ **售后客服智能化** → customer_service模块
+✅ **企业内部知识协同** → knowledge + agent模块
+
+### 2. 实现"一个底座、多种智能体"
+
+- **统一的智能体管理平台**(agent模块)
+- **共享的知识库体系**(knowledge模块)
+- **统一的权限和用户管理**(sys模块)
+- **统一的文件和消息服务**(file、message模块)
+
+### 3. 可扩展性设计
+
+- **新增智能体类型**:在agent.tb_agent中添加新的agent_type
+- **新增业务模块**:创建新的Schema和表
+- **集成外部系统**:通过API集成表和CRM配置表
+- **自定义权限**:通过ACL表实现任意对象的权限控制
+
+---
+
+## 🚀 部署指南
+
+### 快速部署
+
+```bash
+# 进入SQL脚本目录
+cd urbanLifelineServ\.bin\database\postgres\sql
+
+# 1. 创建数据库
+createdb urbanlifeline
+
+# 2. 执行完整初始化脚本(推荐)
+psql -d urbanlifeline -f createTableAll.sql
+
+# 或者手动按顺序执行
+psql -d urbanlifeline -f createTablePermission.sql
+psql -d urbanlifeline -f createTableUser.sql
+psql -d urbanlifeline -f createTableFile.sql
+psql -d urbanlifeline -f createTableMessage.sql
+psql -d urbanlifeline -f createTableLog.sql
+psql -d urbanlifeline -f createTableConfig.sql
+psql -d urbanlifeline -f createTableKnowledge.sql
+psql -d urbanlifeline -f createTableBidding.sql
+psql -d urbanlifeline -f createTableCustomerService.sql
+psql -d urbanlifeline -f createTableAgent.sql
+
+# 3. 执行优化补丁(可选)
+psql -d urbanlifeline -f optimizations.sql
+
+# 4. 初始化基础数据
+psql -d urbanlifeline -f initDataConfig.sql
+```
+
+### 依赖扩展
+
+```sql
+-- 必需扩展
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- UUID生成
+CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- 全文搜索
+CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- GIN索引支持
+
+-- 可选扩展
+CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- 数据加密
+CREATE EXTENSION IF NOT EXISTS "vector"; -- 向量检索(需单独安装)
+```
+
+---
+
+## 🔍 现有SQL文件的优化建议
+
+### 已实施的优化
+
+✅ **createTableUser.sql**
+- 移除登录日志表的password字段(安全性)
+- 添加用户部门关联表(多对多关系)
+- 添加主部门字段
+
+✅ **createTablePermission.sql**
+- 添加角色排序字段
+- 添加权限类型字段(操作/数据/菜单权限)
+
+✅ **createTableFile.sql**
+- 添加版本管理字段
+- 添加分类和标签
+- 创建文件关联表(统一文件关联机制)
+
+✅ **createTableMessage.sql**
+- 创建消息模板表(支持模板化发送)
+
+✅ **createTableLog.sql**
+- 添加链路追踪字段(trace_id、span_id)
+
+### 建议的补充优化
+
+详见:`optimizations.sql`文件
+
+---
+
+## 📚 参考文档
+
+### 1. 数据库设计文档.md
+
+**内容包含:**
+- 系统概述和架构设计
+- 核心模块详细设计
+- 数据库优化建议
+- 现有SQL文件修改建议
+- 数据安全建议
+- 部署和维护建议
+
+**适用场景:**
+- 系统架构师了解整体设计
+- 开发人员理解业务逻辑
+- DBA进行数据库优化
+
+### 2. 数据库表结构速查.md
+
+**内容包含:**
+- 所有模块的表结构快速参考
+- 常用查询示例
+- 视图和函数速查
+- 索引策略说明
+- 性能优化建议
+
+**适用场景:**
+- 日常开发查询表结构
+- 编写业务SQL
+- 快速定位相关表
+
+### 3. 功能结构.xml
+
+**原始需求文档:**
+- 系统目标定义
+- 四大业务模块功能划分
+- 平台基础设施模块
+- Draw.io格式的架构图
+
+---
+
+## ✅ 验证检查清单
+
+### 数据完整性
+
+- [x] 所有表都有主键
+- [x] 外键关系正确定义
+- [x] 唯一约束合理设置
+- [x] 非空约束符合业务逻辑
+
+### 性能优化
+
+- [x] 高频查询字段都有索引
+- [x] 外键字段都有索引
+- [x] 使用部分索引减少索引大小
+- [x] JSONB和数组字段使用GIN索引
+
+### 安全性
+
+- [x] 敏感字段移除或加密
+- [x] 软删除机制
+- [x] 审计日志支持
+- [x] 行级安全策略(可选启用)
+
+### 可维护性
+
+- [x] 表名和字段名规范统一
+- [x] 所有表和字段都有注释
+- [x] 触发器自动维护update_time
+- [x] 提供数据归档函数
+
+### 可扩展性
+
+- [x] Schema分离便于模块扩展
+- [x] JSONB字段支持灵活扩展
+- [x] 标签和分类字段
+- [x] 元数据字段预留
+
+---
+
+## 📊 后续优化方向
+
+### 短期(1-3个月)
+
+1. **向量检索优化**
+ - 安装pgvector扩展
+ - 优化embedding索引参数
+ - 实现混合检索(向量+全文)
+
+2. **监控告警**
+ - 集成pgBadger日志分析
+ - 配置慢查询告警
+ - 设置表膨胀告警
+
+3. **数据初始化**
+ - 编写初始化数据脚本
+ - 创建测试数据集
+ - 准备演示数据
+
+### 中期(3-6个月)
+
+1. **性能优化**
+ - 分析慢查询并优化
+ - 实施分区表策略
+ - 创建物化视图
+
+2. **读写分离**
+ - 配置主从复制
+ - 实现读写分离中间件
+ - 优化读库查询
+
+3. **备份策略**
+ - 实施全量+增量备份
+ - 配置WAL归档
+ - 演练灾难恢复
+
+### 长期(6-12个月)
+
+1. **分布式部署**
+ - 评估Citus扩展
+ - 设计分片策略
+ - 实现水平扩展
+
+2. **多数据中心**
+ - 配置异地备份
+ - 实现数据同步
+ - 制定灾备方案
+
+3. **智能优化**
+ - 自动索引推荐
+ - 查询性能自动优化
+ - 存储自动扩容
+
+---
+
+## 🎓 技术栈总结
+
+### 数据库技术
+
+- **PostgreSQL 14+**:主数据库
+- **pgvector**:向量检索(可选)
+- **pg_trgm**:全文搜索
+- **JSONB**:半结构化数据存储
+- **Row Level Security**:行级安全策略
+
+### 设计模式
+
+- **RBAC + ACL**:混合权限模型
+- **软删除**:数据安全保护
+- **多租户**:部门路径隔离
+- **审计追踪**:完整的变更记录
+- **事件驱动**:触发器自动化
+
+### 性能优化
+
+- **索引策略**:B-Tree + GIN + 部分索引
+- **查询优化**:视图、CTE、窗口函数
+- **数据归档**:自动归档函数
+- **分区表**:时间序列数据分区
+
+---
+
+## 📞 技术支持
+
+### 问题反馈
+
+如在使用过程中遇到问题,请提供:
+1. 问题描述
+2. 错误信息
+3. 执行的SQL语句
+4. PostgreSQL版本
+
+### 文档维护
+
+本文档随数据库设计持续更新:
+- **当前版本**:v1.0
+- **更新日期**:2024-12-02
+- **维护团队**:泰豪电源AI数智化平台开发组
+
+---
+
+## 🎉 交付完成
+
+✅ **50+张核心业务表设计完成**
+✅ **12个SQL脚本文件交付**
+✅ **3份完整技术文档**
+✅ **覆盖4大业务模块**
+✅ **支持"一个底座、多种智能体"架构**
+
+**数据库设计已完全就绪,可直接用于项目开发!** 🚀
+
+---
+
+**感谢使用!**
diff --git a/docs/数据库设计文档.md b/docs/数据库设计文档.md
new file mode 100644
index 0000000..b09e08c
--- /dev/null
+++ b/docs/数据库设计文档.md
@@ -0,0 +1,698 @@
+# 泰豪电源AI数智化平台 - 数据库设计文档
+
+## 一、系统概述
+
+基于功能结构.xml的系统架构设计,本数据库设计遵循"一个底座、多种智能体"的核心理念,支持四大业务模块:
+
+1. **资料管理智能化**
+2. **招投标自动化**
+3. **售后客服智能化**
+4. **企业内部知识协同**
+
+## 二、数据库架构设计
+
+### 2.1 Schema划分
+
+采用多Schema架构,按业务模块逻辑隔离:
+
+| Schema | 说明 | 核心表数量 |
+|--------|------|-----------|
+| `sys` | 系统基础模块(用户、角色、权限、部门) | 11 |
+| `file` | 文件管理模块 | 1 |
+| `message` | 消息通知模块 | 4 |
+| `log` | 日志审计模块 | 1 |
+| `config` | 系统配置模块 | 1 |
+| `knowledge` | 知识库管理模块 | 4 |
+| `bidding` | 招投标智能体业务模块 | 8 |
+| `customer_service` | 智能客服系统业务模块 | 9 |
+| `agent` | 智能体管理和平台基础设施模块 | 11 |
+
+**总计:50张核心业务表**
+
+## 三、核心模块详细设计
+
+### 3.1 系统基础模块 (sys)
+
+#### 3.1.1 权限体系设计
+
+采用**RBAC(基于角色的访问控制)+ ACL(访问控制列表)**混合模型:
+
+**核心表:**
+- `tb_sys_user` - 用户表
+- `tb_sys_role` - 角色表(支持全局角色和部门私有角色)
+- `tb_sys_permission` - 权限表
+- `tb_sys_dept` - 部门表(树形结构)
+- `tb_sys_acl` - 通用对象级权限表(支持细粒度权限控制)
+- `tb_sys_acl_policy` - ACL策略表(层级可见/可编辑规则)
+
+**设计亮点:**
+1. **多租户支持**:通过`dept_path`字段实现数据隔离,支持部门级多租户
+2. **角色作用域**:`scope`字段区分全局角色和部门私有角色
+3. **细粒度权限**:ACL表支持对任意对象类型(文章、文件、课程等)的权限控制
+4. **层级权限继承**:`include_descendants`支持子部门/子角色权限继承
+
+#### 3.1.2 用户信息管理
+
+```sql
+-- 用户表结构优势
+tb_sys_user:
+ - 支持多种登录方式(email、phone、wechat_id)
+ - 密码加密存储(建议bcrypt/argon2)
+ - 软删除机制(deleted + delete_time)
+ - 时区感知时间戳(timestamptz)
+```
+
+### 3.2 知识库管理模块 (knowledge)
+
+#### 3.2.1 表结构设计
+
+**核心表:**
+- `tb_knowledge_base` - 知识库定义表
+- `tb_knowledge_document` - 知识文档表
+- `tb_knowledge_chunk` - 文档片段表(RAG检索)
+- `tb_knowledge_access_log` - 访问日志表
+
+**设计亮点:**
+
+1. **多类型知识库支持**
+```sql
+kb_type:
+ - bidding -- 招投标知识库
+ - customer_service -- 客服知识库
+ - internal -- 内部协同知识库
+```
+
+2. **文档智能处理**
+ - 自动分类(`category`字段)
+ - AI摘要生成(`content_summary`)
+ - 关键词提取(`keywords`数组)
+ - 向量化状态跟踪(`embedding_status`)
+
+3. **RAG检索支持**
+ - `tb_knowledge_chunk`表存储文档切片
+ - `embedding`字段存储向量(需pgvector扩展)
+ - 支持语义检索和混合检索
+
+4. **版本管理**
+ - `version`字段跟踪文档版本
+ - `parent_doc_id`构建版本树
+
+### 3.3 招投标智能体业务模块 (bidding)
+
+#### 3.3.1 核心业务流程
+
+```
+项目创建 → 文件采集 → 智能解读 → 要素提取 → 投标文件生成 → 审核提交 → 流程跟踪
+```
+
+#### 3.3.2 表结构设计
+
+**核心表:**
+1. `tb_bidding_project` - 招标项目表(主表)
+2. `tb_bidding_document` - 招标文件表
+3. `tb_bidding_requirement` - 要素提取表
+4. `tb_bid_response` - 投标文件生成表
+5. `tb_bidding_scoring_rule` - 评分规则表
+6. `tb_bidding_process` - 流程节点表
+7. `tb_bid_template` - 投标模板表
+
+**设计亮点:**
+
+1. **完整项目生命周期管理**
+```sql
+project_status:
+ - collecting -- 收集中
+ - analyzing -- 分析中
+ - preparing -- 准备投标
+ - submitted -- 已提交
+ - opened -- 已开标
+ - won/lost -- 中标/未中标
+ - abandoned -- 放弃
+```
+
+2. **智能要素提取**
+ - 支持7大类要素(商务、技术、否决项、资质、交付、付款、评分)
+ - AI提取置信度跟踪
+ - 来源位置记录(页码、段落)
+ - 合规状态跟踪
+
+3. **投标文件生成**
+ - 支持多种生成方式(AI生成、模板生成、人工编写)
+ - 版本管理(`parent_version_id`)
+ - 审批流程状态跟踪
+
+4. **评分规则智能分析**
+ - 多维度评分(技术分、商务分、价格分、信誉分)
+ - 公式计算支持
+ - 得分预估和优化建议
+
+### 3.4 智能客服系统业务模块 (customer_service)
+
+#### 3.4.1 核心业务流程
+
+```
+客户咨询 → AI问答 → 人工转接 → 工单生成 → 工单处理 → CRM同步 → 满意度评价
+```
+
+#### 3.4.2 表结构设计
+
+**核心表:**
+1. `tb_customer` - 客户信息表
+2. `tb_conversation` - 会话表
+3. `tb_conversation_message` - 会话消息表
+4. `tb_ticket` - 工单表
+5. `tb_ticket_log` - 工单处理记录表
+6. `tb_faq` - FAQ表
+7. `tb_service_evaluation` - 客服评价表
+8. `tb_crm_config` - CRM集成配置表
+
+**设计亮点:**
+
+1. **全渠道客户管理**
+ - 支持多渠道(微信、网页、APP、电话)
+ - 客户等级分层(VIP、重要、普通、潜在)
+ - 标签化管理
+ - CRM系统ID映射
+
+2. **智能会话管理**
+ - AI/人工/转接会话类型
+ - 会话摘要自动生成
+ - 满意度评价跟踪
+ - 消息情感分析和意图识别
+
+3. **工单智能处理**
+ - AI自动生成工单
+ - SLA超时预警
+ - 优先级智能判定
+ - CRM双向同步
+
+4. **知识库引用跟踪**
+ - `kb_references`字段记录引用的知识库文档
+ - 支持回答溯源
+ - 知识库质量评估数据
+
+### 3.5 智能体管理模块 (agent)
+
+#### 3.5.1 核心功能
+
+```
+智能体定义 → 工具集成 → API管理 → 会话管理 → 监控运维 → 评价反馈
+```
+
+#### 3.5.2 表结构设计
+
+**核心表:**
+1. `tb_agent` - 智能体定义表
+2. `tb_agent_session` - 智能体会话表
+3. `tb_agent_message` - 智能体消息表
+4. `tb_agent_tool` - 智能体工具表
+5. `tb_api_integration` - API集成注册表
+6. `tb_api_call_log` - API调用日志表
+7. `tb_agent_metrics` - 智能体监控指标表
+8. `tb_agent_error_log` - 智能体异常日志表
+9. `tb_agent_rating` - 智能体评价表
+
+**设计亮点:**
+
+1. **智能体广场**
+ - `is_published`字段支持发布到广场
+ - 评分和使用次数统计
+ - 分类和标签管理
+ - 访问级别控制
+
+2. **工具和API集成**
+ - 标准化工具定义(OpenAI Function Calling格式)
+ - 多种认证方式支持
+ - 健康检查机制
+ - 调用日志完整记录
+
+3. **监控和运维**
+ - 按日期/小时的指标聚合
+ - 成本跟踪(Token使用量、API调用费用)
+ - 错误日志和堆栈跟踪
+ - 成功率和响应时间监控
+
+4. **会话管理**
+ - 支持对话、任务、工作流多种类型
+ - 上下文管理(JSONB格式)
+ - Token使用量统计
+ - 知识库引用跟踪
+
+## 四、数据库优化建议
+
+### 4.1 索引优化
+
+#### 4.1.1 已实现的索引策略
+
+1. **主键索引**:所有表都有主键(通常为业务ID)
+2. **唯一索引**:关键业务字段(email、phone、编号等)
+3. **外键索引**:关联查询字段
+4. **复合索引**:高频查询组合字段
+5. **部分索引**:带WHERE条件的索引(减少索引大小)
+6. **GIN索引**:数组字段(tags、keywords等)
+
+#### 4.1.2 建议增加的索引
+
+```sql
+-- 知识库文档全文搜索索引(需要pg_trgm扩展)
+CREATE INDEX idx_knowledge_doc_title_trgm
+ON knowledge.tb_knowledge_document USING gin(title gin_trgm_ops);
+
+-- 会话消息内容全文搜索
+CREATE INDEX idx_message_content_trgm
+ON customer_service.tb_conversation_message USING gin(content gin_trgm_ops);
+
+-- 招标项目名称搜索
+CREATE INDEX idx_project_name_trgm
+ON bidding.tb_bidding_project USING gin(project_name gin_trgm_ops);
+```
+
+### 4.2 性能优化建议
+
+#### 4.2.1 分区表设计
+
+对于数据量大、查询集中在时间范围的表,建议使用分区:
+
+```sql
+-- 示例:API调用日志按月分区
+CREATE TABLE agent.tb_api_call_log (
+ -- ... 字段定义
+) PARTITION BY RANGE (create_time);
+
+CREATE TABLE agent.tb_api_call_log_2024_01
+PARTITION OF agent.tb_api_call_log
+FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
+```
+
+**建议分区的表:**
+- `agent.tb_api_call_log` - API调用日志
+- `log.tb_sys_log` - 系统日志
+- `knowledge.tb_knowledge_access_log` - 知识访问日志
+- `agent.tb_agent_metrics` - 智能体监控指标(已按日期设计)
+
+#### 4.2.2 JSONB字段优化
+
+```sql
+-- 为JSONB字段创建GIN索引
+CREATE INDEX idx_agent_model_config_gin
+ON agent.tb_agent(model_config) USING gin;
+
+-- 为JSONB内特定键创建表达式索引
+CREATE INDEX idx_agent_config_model_name
+ON agent.tb_agent((model_config->>'model_name'));
+```
+
+#### 4.2.3 数组字段优化
+
+```sql
+-- 使用GIN索引支持数组查询
+CREATE INDEX idx_kb_doc_tags_gin
+ON knowledge.tb_knowledge_document USING gin(tags);
+
+-- 查询示例
+SELECT * FROM knowledge.tb_knowledge_document
+WHERE tags @> ARRAY['技术文档'];
+```
+
+### 4.3 数据归档策略
+
+#### 4.3.1 软删除设计
+
+当前设计使用软删除(`deleted`字段),建议:
+
+1. **定期归档**:将deleted=true且超过6个月的数据移至归档表
+2. **归档表命名**:`tb_xxx_archived`
+3. **保留必要索引**:归档表仅保留时间范围查询索引
+
+#### 4.3.2 日志数据归档
+
+```sql
+-- 日志归档策略
+-- 1. API调用日志:保留近3个月,其余归档
+-- 2. 系统日志:保留近6个月,其余归档
+-- 3. 访问日志:保留近1个月,其余归档
+
+-- 归档函数示例
+CREATE OR REPLACE FUNCTION archive_old_logs()
+RETURNS void AS $$
+BEGIN
+ -- 归档API调用日志
+ INSERT INTO agent.tb_api_call_log_archived
+ SELECT * FROM agent.tb_api_call_log
+ WHERE create_time < now() - interval '3 months';
+
+ DELETE FROM agent.tb_api_call_log
+ WHERE create_time < now() - interval '3 months';
+END;
+$$ LANGUAGE plpgsql;
+```
+
+### 4.4 查询优化建议
+
+#### 4.4.1 常用查询模式
+
+```sql
+-- 1. 用户权限检查(高频查询)
+-- 建议:创建物化视图
+CREATE MATERIALIZED VIEW sys.mv_user_permissions AS
+SELECT
+ ur.user_id,
+ array_agg(DISTINCT p.code) AS permissions
+FROM sys.tb_sys_user_role ur
+JOIN sys.tb_sys_role_permission rp ON ur.role_id = rp.role_id
+JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
+WHERE ur.deleted = false AND rp.deleted = false
+GROUP BY ur.user_id;
+
+CREATE UNIQUE INDEX ON sys.mv_user_permissions(user_id);
+
+-- 定期刷新
+REFRESH MATERIALIZED VIEW CONCURRENTLY sys.mv_user_permissions;
+```
+
+#### 4.4.2 复杂统计查询优化
+
+```sql
+-- 使用CTE和窗口函数优化统计查询
+WITH daily_stats AS (
+ SELECT
+ DATE(create_time) AS stat_date,
+ agent_id,
+ COUNT(*) AS session_count,
+ SUM(message_count) AS total_messages
+ FROM agent.tb_agent_session
+ WHERE deleted = false
+ GROUP BY DATE(create_time), agent_id
+)
+SELECT
+ agent_id,
+ stat_date,
+ session_count,
+ total_messages,
+ SUM(session_count) OVER (
+ PARTITION BY agent_id
+ ORDER BY stat_date
+ ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
+ ) AS rolling_7day_sessions
+FROM daily_stats;
+```
+
+## 五、现有SQL文件修改建议
+
+### 5.1 createTableUser.sql 优化
+
+**当前问题:**
+1. 登录日志表中存储密码不安全
+2. 缺少用户部门关联
+
+**建议修改:**
+
+```sql
+-- 1. 移除登录日志表的password字段
+ALTER TABLE sys.tb_sys_login_log DROP COLUMN IF EXISTS password;
+
+-- 2. 添加用户部门关联表
+CREATE TABLE sys.tb_sys_user_dept (
+ optsn VARCHAR(50) NOT NULL,
+ user_id VARCHAR(50) NOT NULL,
+ dept_id VARCHAR(50) NOT NULL,
+ is_primary BOOLEAN DEFAULT false, -- 是否主部门
+ position VARCHAR(100), -- 职位
+ creator VARCHAR(50) DEFAULT NULL,
+ create_time timestamptz NOT NULL DEFAULT now(),
+ update_time timestamptz DEFAULT NULL,
+ deleted BOOLEAN NOT NULL DEFAULT false,
+ PRIMARY KEY (user_id, dept_id),
+ UNIQUE (optsn)
+);
+
+-- 3. 用户表添加默认部门字段
+ALTER TABLE sys.tb_sys_user
+ADD COLUMN IF NOT EXISTS primary_dept_id VARCHAR(50);
+```
+
+### 5.2 createTablePermission.sql 优化
+
+**建议修改:**
+
+```sql
+-- 1. 角色表添加排序字段
+ALTER TABLE sys.tb_sys_role
+ADD COLUMN IF NOT EXISTS order_num INTEGER DEFAULT 0;
+
+-- 2. 权限表添加权限类型字段
+ALTER TABLE sys.tb_sys_permission
+ADD COLUMN IF NOT EXISTS permission_type VARCHAR(20) DEFAULT 'action';
+-- permission_type: action-操作权限/data-数据权限/menu-菜单权限
+
+COMMENT ON COLUMN sys.tb_sys_permission.permission_type
+IS '权限类型:action-操作权限/data-数据权限/menu-菜单权限';
+```
+
+### 5.3 createTableFile.sql 扩展
+
+**建议添加:**
+
+```sql
+-- 1. 文件版本管理
+ALTER TABLE file.tb_sys_file
+ADD COLUMN IF NOT EXISTS version VARCHAR(20) DEFAULT '1.0',
+ADD COLUMN IF NOT EXISTS parent_file_id VARCHAR(50),
+ADD COLUMN IF NOT EXISTS is_latest BOOLEAN DEFAULT true;
+
+-- 2. 文件分类
+ALTER TABLE file.tb_sys_file
+ADD COLUMN IF NOT EXISTS category VARCHAR(100),
+ADD COLUMN IF NOT EXISTS tags TEXT[];
+
+-- 3. 文件关联(支持文件和业务对象关联)
+CREATE TABLE file.tb_file_relation (
+ optsn VARCHAR(50) NOT NULL,
+ relation_id VARCHAR(50) NOT NULL,
+ file_id VARCHAR(50) NOT NULL,
+ object_type VARCHAR(50) NOT NULL, -- 对象类型:bidding_project/ticket/document等
+ object_id VARCHAR(50) NOT NULL, -- 对象ID
+ relation_type VARCHAR(30) DEFAULT 'attachment', -- 关联类型:attachment-附件/avatar-头像/banner-横幅
+ order_num INTEGER DEFAULT 0,
+ creator VARCHAR(50) DEFAULT NULL,
+ create_time timestamptz NOT NULL DEFAULT now(),
+ deleted BOOLEAN NOT NULL DEFAULT false,
+ PRIMARY KEY (relation_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (file_id) REFERENCES file.tb_sys_file(file_id)
+);
+
+CREATE INDEX idx_file_relation_object
+ON file.tb_file_relation(object_type, object_id) WHERE deleted = false;
+```
+
+### 5.4 createTableMessage.sql 增强
+
+**建议添加:**
+
+```sql
+-- 1. 消息模板表
+CREATE TABLE message.tb_message_template (
+ optsn VARCHAR(50) NOT NULL,
+ template_id VARCHAR(50) NOT NULL,
+ template_code VARCHAR(100) NOT NULL, -- 模板编码
+ template_name VARCHAR(255) NOT NULL, -- 模板名称
+ template_type VARCHAR(30) NOT NULL, -- 模板类型:system-系统/business-业务
+ title_template TEXT, -- 标题模板(支持变量)
+ content_template TEXT NOT NULL, -- 内容模板(支持变量)
+ variables JSONB, -- 变量定义
+ dept_path VARCHAR(255) DEFAULT NULL,
+ creator VARCHAR(50) DEFAULT NULL,
+ updater VARCHAR(50) DEFAULT NULL,
+ create_time timestamptz NOT NULL DEFAULT now(),
+ update_time timestamptz DEFAULT NULL,
+ deleted BOOLEAN NOT NULL DEFAULT false,
+ PRIMARY KEY (template_id),
+ UNIQUE (optsn),
+ UNIQUE (template_code)
+);
+
+COMMENT ON TABLE message.tb_message_template IS '消息模板表';
+```
+
+## 六、数据安全建议
+
+### 6.1 敏感数据加密
+
+**需要加密的字段:**
+1. `sys.tb_sys_user.password` - 用户密码(使用bcrypt/argon2)
+2. `agent.tb_agent_tool.auth_config` - API认证配置
+3. `agent.tb_api_integration.auth_config` - 集成认证配置
+4. `customer_service.tb_crm_config.api_key` - CRM API密钥
+
+**建议使用PostgreSQL的pgcrypto扩展:**
+
+```sql
+CREATE EXTENSION IF NOT EXISTS pgcrypto;
+
+-- 加密示例
+INSERT INTO agent.tb_agent_tool (auth_config)
+VALUES (pgp_sym_encrypt('{"api_key": "secret"}', 'encryption_key'));
+
+-- 解密示例
+SELECT pgp_sym_decrypt(auth_config::bytea, 'encryption_key')
+FROM agent.tb_agent_tool;
+```
+
+### 6.2 行级安全策略(RLS)
+
+```sql
+-- 启用行级安全
+ALTER TABLE knowledge.tb_knowledge_document ENABLE ROW LEVEL SECURITY;
+
+-- 创建策略:用户只能访问自己部门的文档
+CREATE POLICY dept_isolation_policy ON knowledge.tb_knowledge_document
+FOR SELECT
+USING (
+ dept_path LIKE (
+ SELECT dept_path || '%'
+ FROM sys.tb_sys_user
+ WHERE user_id = current_setting('app.current_user_id')
+ )
+);
+```
+
+### 6.3 审计日志增强
+
+```sql
+-- 添加审计触发器
+CREATE OR REPLACE FUNCTION audit_trigger_func()
+RETURNS TRIGGER AS $$
+BEGIN
+ IF (TG_OP = 'DELETE') THEN
+ INSERT INTO log.tb_sys_log (
+ log_id, type, level, module, message, data, creator
+ ) VALUES (
+ gen_random_uuid()::text,
+ 'audit',
+ 'info',
+ TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
+ 'Record deleted',
+ row_to_json(OLD),
+ current_setting('app.current_user_id', true)
+ );
+ RETURN OLD;
+ ELSIF (TG_OP = 'UPDATE') THEN
+ INSERT INTO log.tb_sys_log (
+ log_id, type, level, module, message, data, creator
+ ) VALUES (
+ gen_random_uuid()::text,
+ 'audit',
+ 'info',
+ TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
+ 'Record updated',
+ jsonb_build_object('old', row_to_json(OLD), 'new', row_to_json(NEW)),
+ current_setting('app.current_user_id', true)
+ );
+ RETURN NEW;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+
+-- 为敏感表添加审计触发器
+CREATE TRIGGER audit_user_changes
+AFTER UPDATE OR DELETE ON sys.tb_sys_user
+FOR EACH ROW EXECUTE FUNCTION audit_trigger_func();
+```
+
+## 七、部署和维护建议
+
+### 7.1 初始化顺序
+
+```bash
+# 1. 创建扩展
+psql -d urbanlifeline -f extensions.sql
+
+# 2. 按依赖顺序执行建表脚本
+psql -d urbanlifeline -f createTablePermission.sql
+psql -d urbanlifeline -f createTableUser.sql
+psql -d urbanlifeline -f createTableFile.sql
+psql -d urbanlifeline -f createTableMessage.sql
+psql -d urbanlifeline -f createTableLog.sql
+psql -d urbanlifeline -f createTableConfig.sql
+psql -d urbanlifeline -f createTableKnowledge.sql
+psql -d urbanlifeline -f createTableBidding.sql
+psql -d urbanlifeline -f createTableCustomerService.sql
+psql -d urbanlifeline -f createTableAgent.sql
+
+# 3. 执行完整初始化(包含触发器和视图)
+psql -d urbanlifeline -f createTableAll.sql
+
+# 4. 初始化基础数据
+psql -d urbanlifeline -f initDataConfig.sql
+```
+
+### 7.2 定期维护任务
+
+```sql
+-- 1. 分析和优化表
+ANALYZE VERBOSE;
+VACUUM ANALYZE;
+
+-- 2. 重建索引(处理索引膨胀)
+REINDEX DATABASE urbanlifeline;
+
+-- 3. 更新表统计信息
+VACUUM ANALYZE sys.tb_sys_user;
+VACUUM ANALYZE agent.tb_agent_session;
+
+-- 4. 检查慢查询
+SELECT query, calls, total_time, mean_time
+FROM pg_stat_statements
+ORDER BY mean_time DESC
+LIMIT 20;
+```
+
+### 7.3 备份策略
+
+```bash
+# 全量备份(每日)
+pg_dump -Fc urbanlifeline > backup_$(date +%Y%m%d).dump
+
+# 增量备份(使用WAL归档)
+# 在postgresql.conf中配置:
+# wal_level = replica
+# archive_mode = on
+# archive_command = 'cp %p /path/to/archive/%f'
+
+# 恢复示例
+pg_restore -d urbanlifeline -c backup_20240101.dump
+```
+
+## 八、总结
+
+### 8.1 设计优势
+
+1. **模块化架构**:9个Schema清晰划分业务边界
+2. **可扩展性**:支持多租户、多智能体、多业务类型
+3. **性能优化**:合理的索引设计、分区策略、视图优化
+4. **数据安全**:软删除、审计日志、权限控制、数据加密
+5. **业务完整**:覆盖招投标、客服、知识库全流程
+
+### 8.2 后续优化方向
+
+1. **向量检索优化**:引入pgvector扩展,优化RAG检索性能
+2. **分布式部署**:考虑Citus扩展实现水平扩展
+3. **实时数据同步**:使用Logical Replication实现异地备份
+4. **监控告警**:集成pgBadger、pg_stat_statements等工具
+5. **读写分离**:主从复制实现读写分离
+
+### 8.3 关键指标
+
+- **总表数**:50+ 张核心表
+- **Schema数**:9 个业务Schema
+- **索引类型**:B-Tree、GIN、部分索引、表达式索引
+- **数据类型**:标准类型 + JSONB + 数组 + 向量(可选)
+- **时区支持**:全部使用timestamptz
+- **软删除**:全表支持deleted标记
+
+---
+
+**文档版本**: 1.0
+**最后更新**: 2024-12-02
+**维护团队**: 泰豪电源AI数智化平台开发组
diff --git a/urbanLifelineServ/.bin/database/postgres/bin.sh b/urbanLifelineServ/.bin/database/postgres/bin.sh
new file mode 100644
index 0000000..3674018
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/bin.sh
@@ -0,0 +1,270 @@
+
+#!/bin/bash
+
+# 定义颜色输出
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# 设置脚本所在目录为工作目录
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+SQL_DIR="${SCRIPT_DIR}/sql"
+
+# 打印带时间戳的日志
+log() {
+ local level=$1
+ local message=$2
+ local color=$NC
+
+ case $level in
+ "INFO") color=$BLUE;;
+ "SUCCESS") color=$GREEN;;
+ "WARN") color=$YELLOW;;
+ "ERROR") color=$RED;;
+ esac
+
+ echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] ${color}${level}${NC}: ${message}"
+}
+
+# 数据库连接信息(可通过环境变量覆盖)
+DB_HOST=${POSTGRES_HOST:-"localhost"}
+DB_PORT=${POSTGRES_PORT:-"5432"}
+DB_NAME=${POSTGRES_DB:-"urban-lifeline"}
+DB_USER=${POSTGRES_USER:-"postgres"}
+DB_PASSWORD=${POSTGRES_PASSWORD:-"postgres"}
+
+# 设置 PSQL 环境变量以支持中文
+export PGCLIENTENCODING=UTF8
+
+# 检查psql命令是否可用
+check_psql() {
+ if ! command -v psql &> /dev/null; then
+ echo -e "${RED}Error: psql command not found. Please install PostgreSQL client.${NC}"
+ exit 1
+ fi
+}
+
+# 检查并创建数据库用户
+check_and_create_user() {
+ local new_user=$1
+ local new_password=$2
+
+ # 使用 postgres 用户执行
+ if sudo -u postgres psql -c "SELECT 1 FROM pg_roles WHERE rolname = '$new_user'" | grep -q 1; then
+ echo -e "${GREEN}User $new_user already exists${NC}"
+ else
+ echo -e "${YELLOW}Creating user $new_user...${NC}"
+ sudo -u postgres psql -c "CREATE USER $new_user WITH PASSWORD '$new_password' CREATEDB;"
+ if [ $? -eq 0 ]; then
+ echo -e "${GREEN}User $new_user created successfully${NC}"
+ else
+ echo -e "${RED}Failed to create user $new_user${NC}"
+ exit 1
+ fi
+ fi
+}
+
+# 检查数据库连接
+check_db_connection() {
+ # 首先尝试以当前用户身份连接
+ if ! psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -c '\q' &> /dev/null; then
+ echo -e "${YELLOW}Could not connect with current settings, attempting to create user...${NC}"
+ # 创建用户并设置权限
+ check_and_create_user "$DB_USER" "$DB_PASSWORD"
+
+ # 再次检查连接
+ if ! psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -c '\q' &> /dev/null; then
+ echo -e "${RED}Error: Could not connect to PostgreSQL server.${NC}"
+ echo "Please check your connection settings:"
+ echo "Host: $DB_HOST"
+ echo "Port: $DB_PORT"
+ echo "User: $DB_USER"
+ exit 1
+ fi
+ fi
+}
+
+# 执行SQL文件
+execute_sql_file() {
+ local sql_file=$1
+ if [ ! -f "$sql_file" ]; then
+ echo -e "${RED}Error: SQL file not found: $sql_file${NC}"
+ return 1
+ fi
+
+ echo -e "${YELLOW}Executing SQL file: $sql_file${NC}"
+ PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$sql_file"
+ local status=$?
+ if [ $status -eq 0 ]; then
+ echo -e "${GREEN}Successfully executed: $sql_file${NC}"
+ else
+ echo -e "${RED}Failed to execute: $sql_file${NC}"
+ return $status
+ fi
+}
+
+# 初始化数据库
+init() {
+ echo -e "${YELLOW}Initializing database...${NC}"
+
+ # 执行完整的初始化脚本
+ log "INFO" "Executing initialization script..."
+ # Run from inside the SQL_DIR so relative \i includes in initAll.sql (like createDB.sql)
+ # resolve relative to the SQL directory.
+ (
+ if [ ! -d "$SQL_DIR" ]; then
+ echo -e "${RED}Error: SQL directory not found: $SQL_DIR${NC}"
+ exit 1
+ fi
+ cd "$SQL_DIR" || exit 1
+ PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -v ON_ERROR_STOP=1 -f "initAll.sql"
+ )
+
+ if [ $? -eq 0 ]; then
+ log "SUCCESS" "Database initialization completed successfully"
+ else
+ log "ERROR" "Database initialization failed"
+ return 1
+ fi
+
+ if [ $? -ne 0 ]; then
+ echo -e "${RED}Failed to create database.${NC}"
+ return 1
+ fi
+
+ # 2. 创建扩展和设置搜索路径
+ echo -e "${YELLOW}Creating extensions...${NC}"
+ check_extensions_availability() {
+ # 检查服务器上是否存在需创建的扩展
+ local missing=()
+ local exts=("uuid-ossp" "pg_trgm" "btree_gist")
+ for ext in "${exts[@]}"; do
+ # 查询 pg_available_extensions 来判断扩展是否已安装到服务器目录
+ if ! PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '$ext';" | grep -q 1; then
+ missing+=("$ext")
+ fi
+ done
+
+ if [ ${#missing[@]} -ne 0 ]; then
+ echo -e "${RED}Error: The following server-side extensions are not available: ${missing[*]}${NC}"
+ echo "If you compiled PostgreSQL from source, you need to build and install the contrib modules into the server's installation prefix. Example steps:"
+ echo " # 在 PostgreSQL 源码目录下运行:"
+ echo " cd /path/to/postgresql-source/contrib"
+ echo " make"
+ echo " sudo make install"
+ echo "或者只安装缺失的模块(例如 uuid-ossp):"
+ echo " cd /path/to/postgresql-source/contrib/uuid-ossp"
+ echo " make"
+ echo " sudo make install"
+ echo "安装完成后,重启 PostgreSQL 服务并重新运行此脚本:"
+ echo " sudo systemctl restart postgresql"
+ echo "如果你使用的是容器或自定义路径,请确保将编译安装的扩展安装到 PostgreSQL 的 \$(pg_config --sharedir)/extension 目录下。"
+ return 1
+ fi
+ return 0
+ }
+
+ # 检查扩展可用性,若缺失则给出建议并退出
+ if ! check_extensions_availability; then
+ return 1
+ fi
+
+ PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "
+ CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";
+ CREATE EXTENSION IF NOT EXISTS \"pg_trgm\";
+ CREATE EXTENSION IF NOT EXISTS \"btree_gist\";"
+
+ if [ $? -ne 0 ]; then
+ echo -e "${RED}Failed to create extensions.${NC}"
+ return 1
+ fi
+
+ # 3. 逐个执行初始化SQL文件
+ echo -e "${YELLOW}Initializing tables...${NC}"
+ while IFS= read -r line || [[ -n "$line" ]]; do
+ # 跳过注释和空行
+ [[ $line =~ ^--.*$ ]] && continue
+ [[ -z "${line// }" ]] && continue
+
+ # 从 \i 命令中提取文件名
+ if [[ $line =~ \\i[[:space:]]+([^[:space:]]+) ]]; then
+ sql_file="${SQL_DIR}/${BASH_REMATCH[1]}"
+ if [[ $sql_file != *"createDB.sql"* ]]; then # 跳过createDB.sql
+ echo -e "${YELLOW}Executing: $sql_file${NC}"
+ PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$sql_file"
+ if [ $? -ne 0 ]; then
+ echo -e "${RED}Failed to execute: $sql_file${NC}"
+ return 1
+ fi
+ fi
+ fi
+ done < "${SQL_DIR}/initAll.sql"
+
+ # 4. 设置搜索路径
+ echo -e "${YELLOW}Setting search path...${NC}"
+ PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "
+ ALTER DATABASE $DB_NAME SET search_path TO sys, public;"
+
+ echo -e "${GREEN}Database initialization completed successfully.${NC}"
+ return 0
+}
+
+# 重新初始化数据库
+reinit() {
+ echo -e "${YELLOW}Reinitializing database...${NC}"
+ delete
+ init
+}
+
+# 删除数据库
+delete() {
+ echo -e "${YELLOW}Deleting database...${NC}"
+
+ # 确保没有活动连接
+ PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -c "
+ SELECT pg_terminate_backend(pg_stat_activity.pid)
+ FROM pg_stat_activity
+ WHERE pg_stat_activity.datname = '$DB_NAME'
+ AND pid <> pg_backend_pid();"
+
+ # 删除数据库
+ PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -c "DROP DATABASE IF EXISTS $DB_NAME;"
+
+ echo -e "${GREEN}Database deleted.${NC}"
+}
+
+# 显示帮助信息
+show_help() {
+ echo "Usage: $0 {init|reinit|delete}"
+ echo "Commands:"
+ echo " init Initialize the database"
+ echo " reinit Reinitialize the database (delete and create)"
+ echo " delete Delete the database"
+}
+
+# 主函数
+main() {
+ check_psql
+ check_db_connection
+
+ case "$1" in
+ "init")
+ init
+ ;;
+ "reinit")
+ reinit
+ ;;
+ "delete")
+ delete
+ ;;
+ *)
+ show_help
+ exit 1
+ ;;
+ esac
+}
+
+# Call main with all passed arguments so the script runs when invoked
+main "$@"
\ No newline at end of file
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createDB.sql b/urbanLifelineServ/.bin/database/postgres/sql/createDB.sql
new file mode 100644
index 0000000..51b05a1
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createDB.sql
@@ -0,0 +1,27 @@
+
+-- 删除已存在的数据库(如果存在)
+DROP DATABASE IF EXISTS urban-lifeline;
+
+-- 创建新数据库,使用 UTF8 编码,并设置适合中文的排序规则
+-- 使用 template0 确保干净的数据库模板
+-- zh_CN.UTF-8 支持中文字符排序和比较
+CREATE DATABASE urban-lifeline
+ ENCODING 'UTF8'
+ TEMPLATE template0
+ LC_COLLATE 'zh_CN.UTF-8'
+ LC_CTYPE 'zh_CN.UTF-8';
+
+-- 连接到新创建的数据库
+\c urban-lifeline;
+
+-- -- 创建扩展(如果需要)
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- UUID 支持
+CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- 文本搜索支持
+CREATE EXTENSION IF NOT EXISTS "btree_gist"; -- GiST 索引支持
+
+-- 设置搜索路径(可选,但建议设置)
+-- ALTER DATABASE urban-lifeline SET search_path TO sys, public;
+
+-- sudo ./configure --prefix=/opt/postgres/postgres-17.6
+-- --with-uuid=ossp --with-openssl --with-libxml --with-pam
+-- && sudo make && sudo make install
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableAgent.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableAgent.sql
new file mode 100644
index 0000000..632f498
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableAgent.sql
@@ -0,0 +1,308 @@
+-- =============================
+-- 智能体管理和平台基础设施模块
+-- 支持:智能体广场、API集成管理、智能体运维监控
+-- =============================
+CREATE SCHEMA IF NOT EXISTS agent;
+
+-- 智能体定义表
+DROP TABLE IF EXISTS agent.tb_agent CASCADE;
+CREATE TABLE agent.tb_agent (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ agent_id VARCHAR(50) NOT NULL, -- 智能体ID
+ agent_code VARCHAR(100) NOT NULL, -- 智能体编码(唯一标识)
+ agent_name VARCHAR(255) NOT NULL, -- 智能体名称
+ agent_type VARCHAR(50) NOT NULL, -- 智能体类型:bidding-招投标/customer_service-客服/knowledge_assistant-知识助手/custom-自定义
+ display_name VARCHAR(255) NOT NULL, -- 展示名称
+ description TEXT, -- 智能体描述
+ icon VARCHAR(500), -- 图标URL
+ banner VARCHAR(500), -- Banner图URL
+ version VARCHAR(20) DEFAULT '1.0.0', -- 版本号
+ model_provider VARCHAR(50), -- 模型提供商:openai/anthropic/baidu/aliyun/custom
+ model_name VARCHAR(100), -- 模型名称
+ model_config JSONB, -- 模型配置(温度、最大tokens等)
+ prompt_template TEXT, -- 提示词模板
+ system_prompt TEXT, -- 系统提示词
+ kb_ids VARCHAR(50)[], -- 关联知识库ID数组
+ tool_ids VARCHAR(50)[], -- 关联工具ID数组
+ capabilities TEXT[], -- 能力列表
+ access_level VARCHAR(20) DEFAULT 'private', -- 访问级别:public-公开/private-私有/internal-内部
+ is_published BOOLEAN DEFAULT false, -- 是否发布到智能体广场
+ usage_count INTEGER DEFAULT 0, -- 使用次数
+ rating DECIMAL(3,2) DEFAULT 0, -- 评分(0-5)
+ rating_count INTEGER DEFAULT 0, -- 评分人数
+ tags TEXT[], -- 标签数组
+ category VARCHAR(100), -- 分类
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ owner_user_id VARCHAR(50), -- 所有者用户ID
+ status VARCHAR(20) DEFAULT 'active', -- 状态:active-激活/inactive-停用/under_maintenance-维护中
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (agent_id),
+ UNIQUE (optsn),
+ UNIQUE (agent_code)
+);
+
+CREATE INDEX idx_agent_type ON agent.tb_agent(agent_type) WHERE deleted = false;
+CREATE INDEX idx_agent_published ON agent.tb_agent(is_published) WHERE deleted = false AND is_published = true;
+CREATE INDEX idx_agent_category ON agent.tb_agent(category) WHERE deleted = false;
+
+COMMENT ON TABLE agent.tb_agent IS '智能体定义表';
+COMMENT ON COLUMN agent.tb_agent.agent_type IS '智能体类型:bidding/customer_service/knowledge_assistant/custom';
+
+-- 智能体会话表
+DROP TABLE IF EXISTS agent.tb_agent_session CASCADE;
+CREATE TABLE agent.tb_agent_session (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ session_id VARCHAR(50) NOT NULL, -- 会话ID
+ agent_id VARCHAR(50) NOT NULL, -- 智能体ID
+ user_id VARCHAR(50) NOT NULL, -- 用户ID
+ session_type VARCHAR(30) DEFAULT 'chat', -- 会话类型:chat-对话/task-任务/workflow-工作流
+ session_name VARCHAR(255), -- 会话名称
+ context JSONB, -- 会话上下文
+ session_status VARCHAR(20) DEFAULT 'active', -- 会话状态:active-活跃/paused-暂停/ended-结束
+ start_time timestamptz DEFAULT now(), -- 开始时间
+ end_time timestamptz, -- 结束时间
+ message_count INTEGER DEFAULT 0, -- 消息数量
+ token_usage INTEGER DEFAULT 0, -- Token使用量
+ cost DECIMAL(10,4) DEFAULT 0, -- 成本
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (session_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
+);
+
+CREATE INDEX idx_session_agent ON agent.tb_agent_session(agent_id) WHERE deleted = false;
+CREATE INDEX idx_session_user ON agent.tb_agent_session(user_id) WHERE deleted = false;
+
+COMMENT ON TABLE agent.tb_agent_session IS '智能体会话表';
+
+-- 智能体消息表
+DROP TABLE IF EXISTS agent.tb_agent_message CASCADE;
+CREATE TABLE agent.tb_agent_message (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ message_id VARCHAR(50) NOT NULL, -- 消息ID
+ session_id VARCHAR(50) NOT NULL, -- 会话ID
+ agent_id VARCHAR(50) NOT NULL, -- 智能体ID
+ role VARCHAR(20) NOT NULL, -- 角色:user-用户/assistant-助手/system-系统/function-函数
+ content TEXT, -- 消息内容
+ content_type VARCHAR(30) DEFAULT 'text', -- 内容类型:text-文本/image-图片/file-文件/structured-结构化数据
+ function_call JSONB, -- 函数调用(JSON格式)
+ function_response JSONB, -- 函数响应
+ token_count INTEGER, -- Token数量
+ model_name VARCHAR(100), -- 使用的模型
+ kb_references JSONB, -- 知识库引用(JSON数组)
+ metadata JSONB, -- 消息元数据
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (message_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (session_id) REFERENCES agent.tb_agent_session(session_id),
+ FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
+);
+
+CREATE INDEX idx_msg_session ON agent.tb_agent_message(session_id, create_time) WHERE deleted = false;
+CREATE INDEX idx_msg_agent ON agent.tb_agent_message(agent_id) WHERE deleted = false;
+
+COMMENT ON TABLE agent.tb_agent_message IS '智能体消息表';
+
+-- 智能体工具表
+DROP TABLE IF EXISTS agent.tb_agent_tool CASCADE;
+CREATE TABLE agent.tb_agent_tool (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ tool_id VARCHAR(50) NOT NULL, -- 工具ID
+ tool_code VARCHAR(100) NOT NULL, -- 工具编码
+ tool_name VARCHAR(255) NOT NULL, -- 工具名称
+ tool_type VARCHAR(50) NOT NULL, -- 工具类型:api-API调用/function-函数/plugin-插件/integration-集成
+ description TEXT, -- 工具描述
+ function_schema JSONB, -- 函数Schema(OpenAI function calling格式)
+ api_endpoint VARCHAR(500), -- API端点
+ api_method VARCHAR(10), -- API方法:GET/POST/PUT/DELETE
+ api_headers JSONB, -- API请求头
+ auth_type VARCHAR(30), -- 认证类型:none/api_key/oauth2/bearer
+ auth_config JSONB, -- 认证配置(加密存储)
+ request_template TEXT, -- 请求模板
+ response_template TEXT, -- 响应模板
+ timeout_seconds INTEGER DEFAULT 30, -- 超时时间(秒)
+ retry_count INTEGER DEFAULT 3, -- 重试次数
+ is_enabled BOOLEAN DEFAULT true, -- 是否启用
+ usage_count INTEGER DEFAULT 0, -- 使用次数
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (tool_id),
+ UNIQUE (optsn),
+ UNIQUE (tool_code)
+);
+
+CREATE INDEX idx_tool_type ON agent.tb_agent_tool(tool_type) WHERE deleted = false;
+
+COMMENT ON TABLE agent.tb_agent_tool IS '智能体工具表';
+
+-- API集成注册表
+DROP TABLE IF EXISTS agent.tb_api_integration CASCADE;
+CREATE TABLE agent.tb_api_integration (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ integration_id VARCHAR(50) NOT NULL, -- 集成ID
+ integration_name VARCHAR(255) NOT NULL, -- 集成名称
+ integration_type VARCHAR(50) NOT NULL, -- 集成类型:rest_api/soap/graphql/webhook/mq
+ provider VARCHAR(100), -- 提供商
+ base_url VARCHAR(500), -- 基础URL
+ version VARCHAR(20), -- API版本
+ auth_type VARCHAR(30) DEFAULT 'none', -- 认证类型
+ auth_config JSONB, -- 认证配置(加密存储)
+ endpoints JSONB, -- 端点列表(JSON数组)
+ rate_limit INTEGER, -- 速率限制(请求/秒)
+ timeout_seconds INTEGER DEFAULT 30, -- 超时时间
+ retry_config JSONB, -- 重试配置
+ health_check_url VARCHAR(500), -- 健康检查URL
+ health_status VARCHAR(20) DEFAULT 'unknown', -- 健康状态:healthy-健康/unhealthy-不健康/unknown-未知
+ last_health_check timestamptz, -- 最后健康检查时间
+ documentation_url VARCHAR(500), -- 文档URL
+ is_enabled BOOLEAN DEFAULT true, -- 是否启用
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (integration_id),
+ UNIQUE (optsn)
+);
+
+CREATE INDEX idx_integration_type ON agent.tb_api_integration(integration_type) WHERE deleted = false;
+CREATE INDEX idx_integration_health ON agent.tb_api_integration(health_status) WHERE deleted = false;
+
+COMMENT ON TABLE agent.tb_api_integration IS 'API集成注册表';
+
+-- API调用日志表
+DROP TABLE IF EXISTS agent.tb_api_call_log CASCADE;
+CREATE TABLE agent.tb_api_call_log (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ log_id VARCHAR(50) NOT NULL, -- 日志ID
+ integration_id VARCHAR(50), -- 集成ID
+ tool_id VARCHAR(50), -- 工具ID
+ agent_id VARCHAR(50), -- 智能体ID
+ session_id VARCHAR(50), -- 会话ID
+ user_id VARCHAR(50), -- 用户ID
+ endpoint VARCHAR(500) NOT NULL, -- 请求端点
+ method VARCHAR(10) NOT NULL, -- 请求方法
+ request_headers JSONB, -- 请求头
+ request_body TEXT, -- 请求体
+ response_status INTEGER, -- 响应状态码
+ response_headers JSONB, -- 响应头
+ response_body TEXT, -- 响应体
+ duration_ms INTEGER, -- 请求耗时(毫秒)
+ is_success BOOLEAN, -- 是否成功
+ error_message TEXT, -- 错误信息
+ retry_count INTEGER DEFAULT 0, -- 重试次数
+ ip_address VARCHAR(45), -- IP地址
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ PRIMARY KEY (log_id),
+ UNIQUE (optsn)
+);
+
+CREATE INDEX idx_api_log_integration ON agent.tb_api_call_log(integration_id, create_time DESC);
+CREATE INDEX idx_api_log_agent ON agent.tb_api_call_log(agent_id, create_time DESC);
+CREATE INDEX idx_api_log_status ON agent.tb_api_call_log(is_success, create_time DESC);
+
+COMMENT ON TABLE agent.tb_api_call_log IS 'API调用日志表';
+
+-- 智能体监控指标表
+DROP TABLE IF EXISTS agent.tb_agent_metrics CASCADE;
+CREATE TABLE agent.tb_agent_metrics (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ metric_id VARCHAR(50) NOT NULL, -- 指标ID
+ agent_id VARCHAR(50) NOT NULL, -- 智能体ID
+ metric_date DATE NOT NULL, -- 指标日期
+ metric_hour INTEGER, -- 指标小时(0-23)
+ total_sessions INTEGER DEFAULT 0, -- 总会话数
+ active_sessions INTEGER DEFAULT 0, -- 活跃会话数
+ total_messages INTEGER DEFAULT 0, -- 总消息数
+ total_tokens BIGINT DEFAULT 0, -- 总Token数
+ total_cost DECIMAL(10,4) DEFAULT 0, -- 总成本
+ avg_response_time INTEGER, -- 平均响应时间(毫秒)
+ success_rate DECIMAL(5,4), -- 成功率
+ error_count INTEGER DEFAULT 0, -- 错误次数
+ api_call_count INTEGER DEFAULT 0, -- API调用次数
+ avg_rating DECIMAL(3,2), -- 平均评分
+ rating_count INTEGER DEFAULT 0, -- 评分数量
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ PRIMARY KEY (metric_id),
+ UNIQUE (optsn),
+ UNIQUE (agent_id, metric_date, metric_hour),
+ FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
+);
+
+CREATE INDEX idx_metrics_agent_date ON agent.tb_agent_metrics(agent_id, metric_date DESC);
+
+COMMENT ON TABLE agent.tb_agent_metrics IS '智能体监控指标表';
+
+-- 智能体异常日志表
+DROP TABLE IF EXISTS agent.tb_agent_error_log CASCADE;
+CREATE TABLE agent.tb_agent_error_log (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ log_id VARCHAR(50) NOT NULL, -- 日志ID
+ agent_id VARCHAR(50) NOT NULL, -- 智能体ID
+ session_id VARCHAR(50), -- 会话ID
+ error_type VARCHAR(50) NOT NULL, -- 错误类型:model_error-模型错误/api_error-API错误/timeout-超时/rate_limit-限流/other-其他
+ error_code VARCHAR(50), -- 错误代码
+ error_message TEXT NOT NULL, -- 错误信息
+ stack_trace TEXT, -- 堆栈跟踪
+ request_context JSONB, -- 请求上下文
+ severity VARCHAR(20) DEFAULT 'error', -- 严重级别:critical-致命/error-错误/warning-警告
+ is_resolved BOOLEAN DEFAULT false, -- 是否已解决
+ resolution_notes TEXT, -- 解决方案备注
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ resolve_time timestamptz, -- 解决时间
+ PRIMARY KEY (log_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
+);
+
+CREATE INDEX idx_error_agent ON agent.tb_agent_error_log(agent_id, create_time DESC);
+CREATE INDEX idx_error_severity ON agent.tb_agent_error_log(severity) WHERE is_resolved = false;
+
+COMMENT ON TABLE agent.tb_agent_error_log IS '智能体异常日志表';
+
+-- 智能体评价表
+DROP TABLE IF EXISTS agent.tb_agent_rating CASCADE;
+CREATE TABLE agent.tb_agent_rating (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ rating_id VARCHAR(50) NOT NULL, -- 评价ID
+ agent_id VARCHAR(50) NOT NULL, -- 智能体ID
+ session_id VARCHAR(50), -- 会话ID
+ user_id VARCHAR(50) NOT NULL, -- 用户ID
+ rating INTEGER NOT NULL, -- 评分(1-5星)
+ dimensions JSONB, -- 分维度评分(准确性、速度、友好度等)
+ feedback TEXT, -- 评价反馈
+ tags TEXT[], -- 标签
+ is_anonymous BOOLEAN DEFAULT false, -- 是否匿名
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (rating_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
+);
+
+CREATE INDEX idx_rating_agent ON agent.tb_agent_rating(agent_id) WHERE deleted = false;
+
+COMMENT ON TABLE agent.tb_agent_rating IS '智能体评价表';
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableAll.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableAll.sql
new file mode 100644
index 0000000..f935c55
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableAll.sql
@@ -0,0 +1,209 @@
+ -- =============================
+-- 泰豪电源AI数智化平台 - 完整数据库初始化脚本
+-- 包含所有业务模块的表结构
+-- =============================
+
+-- 安装必要的扩展
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- UUID生成
+CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- 全文搜索
+CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- GIN索引支持
+-- CREATE EXTENSION IF NOT EXISTS "vector"; -- pgvector向量检索(需要单独安装)
+
+-- =============================
+-- 1. 系统基础模块(权限、用户、部门、角色)
+-- =============================
+\i createTablePermission.sql
+\i createTableUser.sql
+
+-- =============================
+-- 2. 文件管理模块
+-- =============================
+\i createTableFile.sql
+
+-- =============================
+-- 3. 消息通知模块
+-- =============================
+\i createTableMessage.sql
+
+-- =============================
+-- 4. 日志模块
+-- =============================
+\i createTableLog.sql
+
+-- =============================
+-- 5. 配置管理模块
+-- =============================
+\i createTableConfig.sql
+
+-- =============================
+-- 6. 知识库管理模块
+-- =============================
+\i createTableKnowledge.sql
+
+-- =============================
+-- 7. 招投标智能体业务模块
+-- =============================
+\i createTableBidding.sql
+
+-- =============================
+-- 8. 智能客服系统业务模块
+-- =============================
+\i createTableCustomerService.sql
+
+-- =============================
+-- 9. 智能体管理和平台基础设施模块
+-- =============================
+\i createTableAgent.sql
+
+-- =============================
+-- 创建通用触发器函数
+-- =============================
+
+-- 自动更新update_time的触发器函数
+CREATE OR REPLACE FUNCTION public.update_modified_column()
+RETURNS TRIGGER AS $$
+BEGIN
+ NEW.update_time = now();
+ RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+-- 为所有表添加update_time触发器的辅助函数
+CREATE OR REPLACE FUNCTION public.create_update_triggers()
+RETURNS void AS $$
+DECLARE
+ r RECORD;
+BEGIN
+ FOR r IN
+ SELECT schemaname, tablename
+ FROM pg_tables
+ WHERE schemaname IN ('sys', 'file', 'message', 'log', 'config', 'knowledge', 'bidding', 'customer_service', 'agent')
+ AND tablename LIKE 'tb_%'
+ LOOP
+ -- 检查表是否有update_time列
+ IF EXISTS (
+ SELECT 1
+ FROM information_schema.columns
+ WHERE table_schema = r.schemaname
+ AND table_name = r.tablename
+ AND column_name = 'update_time'
+ ) THEN
+ -- 删除已存在的触发器
+ EXECUTE format('DROP TRIGGER IF EXISTS trg_%s_update_time ON %I.%I',
+ r.tablename, r.schemaname, r.tablename);
+
+ -- 创建新触发器
+ EXECUTE format('CREATE TRIGGER trg_%s_update_time
+ BEFORE UPDATE ON %I.%I
+ FOR EACH ROW
+ EXECUTE FUNCTION public.update_modified_column()',
+ r.tablename, r.schemaname, r.tablename);
+
+ RAISE NOTICE 'Created trigger for %.%', r.schemaname, r.tablename;
+ END IF;
+ END LOOP;
+END;
+$$ LANGUAGE plpgsql;
+
+-- 执行触发器创建
+SELECT public.create_update_triggers();
+
+-- =============================
+-- 创建视图
+-- =============================
+
+-- 用户完整信息视图
+CREATE OR REPLACE VIEW sys.v_user_full_info AS
+SELECT
+ u.user_id,
+ u.email,
+ u.phone,
+ u.wechat_id,
+ u.status,
+ ui.avatar,
+ ui.full_name,
+ ui.gender,
+ ui.level,
+ u.create_time,
+ u.update_time
+FROM sys.tb_sys_user u
+LEFT JOIN sys.tb_sys_user_info ui ON u.user_id = ui.user_id
+WHERE u.deleted = false AND (ui.deleted = false OR ui.deleted IS NULL);
+
+-- 用户角色权限视图
+CREATE OR REPLACE VIEW sys.v_user_role_permission AS
+SELECT DISTINCT
+ ur.user_id,
+ r.role_id,
+ r.name AS role_name,
+ p.permission_id,
+ p.code AS permission_code,
+ p.name AS permission_name,
+ m.module_id,
+ m.name AS module_name
+FROM sys.tb_sys_user_role ur
+JOIN sys.tb_sys_role r ON ur.role_id = r.role_id
+JOIN sys.tb_sys_role_permission rp ON r.role_id = rp.role_id
+JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
+LEFT JOIN sys.tb_sys_module m ON p.module_id = m.module_id
+WHERE ur.deleted = false
+ AND r.deleted = false
+ AND rp.deleted = false
+ AND p.deleted = false;
+
+-- 智能体使用统计视图
+CREATE OR REPLACE VIEW agent.v_agent_usage_stats AS
+SELECT
+ a.agent_id,
+ a.agent_name,
+ a.agent_type,
+ COUNT(DISTINCT s.session_id) AS total_sessions,
+ COUNT(DISTINCT s.user_id) AS unique_users,
+ SUM(s.message_count) AS total_messages,
+ AVG(s.token_usage) AS avg_token_usage,
+ AVG(r.rating) AS avg_rating,
+ COUNT(r.rating_id) AS rating_count,
+ MAX(s.start_time) AS last_used_time
+FROM agent.tb_agent a
+LEFT JOIN agent.tb_agent_session s ON a.agent_id = s.agent_id AND s.deleted = false
+LEFT JOIN agent.tb_agent_rating r ON a.agent_id = r.agent_id AND r.deleted = false
+WHERE a.deleted = false
+GROUP BY a.agent_id, a.agent_name, a.agent_type;
+
+-- 客服工单统计视图
+CREATE OR REPLACE VIEW customer_service.v_ticket_stats AS
+SELECT
+ t.ticket_status,
+ t.priority,
+ t.ticket_type,
+ COUNT(*) AS ticket_count,
+ AVG(EXTRACT(EPOCH FROM (t.resolution_time - t.create_time))/3600) AS avg_resolution_hours,
+ AVG(t.customer_rating) AS avg_rating,
+ COUNT(CASE WHEN t.is_overdue THEN 1 END) AS overdue_count
+FROM customer_service.tb_ticket t
+WHERE t.deleted = false
+GROUP BY t.ticket_status, t.priority, t.ticket_type;
+
+-- 招投标项目统计视图
+CREATE OR REPLACE VIEW bidding.v_project_stats AS
+SELECT
+ p.project_status,
+ p.project_type,
+ COUNT(*) AS project_count,
+ SUM(p.budget_amount) AS total_budget,
+ SUM(CASE WHEN p.winning_status = 'won' THEN 1 ELSE 0 END) AS won_count,
+ SUM(CASE WHEN p.winning_status = 'won' THEN p.winning_amount ELSE 0 END) AS total_won_amount,
+ AVG(CASE WHEN p.winning_status = 'won' THEN p.winning_amount / NULLIF(p.budget_amount, 0) ELSE NULL END) AS avg_win_rate
+FROM bidding.tb_bidding_project p
+WHERE p.deleted = false
+GROUP BY p.project_status, p.project_type;
+
+COMMENT ON VIEW sys.v_user_full_info IS '用户完整信息视图';
+COMMENT ON VIEW sys.v_user_role_permission IS '用户角色权限视图';
+COMMENT ON VIEW agent.v_agent_usage_stats IS '智能体使用统计视图';
+COMMENT ON VIEW customer_service.v_ticket_stats IS '客服工单统计视图';
+COMMENT ON VIEW bidding.v_project_stats IS '招投标项目统计视图';
+
+-- =============================
+-- 数据库初始化完成
+-- =============================
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableBidding.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableBidding.sql
new file mode 100644
index 0000000..d32bbc5
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableBidding.sql
@@ -0,0 +1,264 @@
+-- =============================
+-- 招投标智能体业务模块
+-- 支持:招标文件管理、投标文件生成、评分分析、流程跟踪
+-- =============================
+CREATE SCHEMA IF NOT EXISTS bidding;
+
+-- 招标项目表
+DROP TABLE IF EXISTS bidding.tb_bidding_project CASCADE;
+CREATE TABLE bidding.tb_bidding_project (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ project_id VARCHAR(50) NOT NULL, -- 项目ID
+ project_no VARCHAR(100) NOT NULL, -- 项目编号
+ project_name VARCHAR(500) NOT NULL, -- 项目名称
+ project_type VARCHAR(50) NOT NULL, -- 项目类型:public-公开招标/invitation-邀请招标/competitive_negotiation-竞争性谈判
+ industry VARCHAR(100), -- 所属行业
+ source_platform VARCHAR(100), -- 来源平台(如:政府采购网、企业官网等)
+ source_url VARCHAR(500), -- 来源URL
+ publish_date timestamptz, -- 发布日期
+ deadline timestamptz, -- 投标截止日期
+ opening_date timestamptz, -- 开标日期
+ budget_amount DECIMAL(18,2), -- 预算金额
+ currency VARCHAR(10) DEFAULT 'CNY', -- 货币单位
+ project_status VARCHAR(30) NOT NULL DEFAULT 'collecting', -- 项目状态:collecting-收集中/analyzing-分析中/preparing-准备投标/submitted-已提交/opened-已开标/won-中标/lost-未中标/abandoned-放弃
+ winning_status VARCHAR(30), -- 中标状态:pending-待定/won-中标/lost-未中标
+ winning_amount DECIMAL(18,2), -- 中标金额
+ client_name VARCHAR(255), -- 客户名称
+ client_contact VARCHAR(100), -- 客户联系方式
+ contact_person VARCHAR(100), -- 联系人
+ project_location VARCHAR(500), -- 项目地点
+ description TEXT, -- 项目描述
+ keywords TEXT[], -- 关键词数组
+ metadata JSONB DEFAULT NULL, -- 项目元数据
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ responsible_user VARCHAR(50), -- 负责人
+ team_members VARCHAR(50)[], -- 团队成员数组
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (project_id),
+ UNIQUE (optsn),
+ UNIQUE (project_no)
+);
+
+CREATE INDEX idx_project_status ON bidding.tb_bidding_project(project_status) WHERE deleted = false;
+CREATE INDEX idx_project_deadline ON bidding.tb_bidding_project(deadline) WHERE deleted = false;
+CREATE INDEX idx_project_dept ON bidding.tb_bidding_project(dept_path) WHERE deleted = false;
+CREATE INDEX idx_project_responsible ON bidding.tb_bidding_project(responsible_user) WHERE deleted = false;
+
+COMMENT ON TABLE bidding.tb_bidding_project IS '招标项目表';
+COMMENT ON COLUMN bidding.tb_bidding_project.project_status IS '项目状态:collecting/analyzing/preparing/submitted/opened/won/lost/abandoned';
+
+-- 招标文件表
+DROP TABLE IF EXISTS bidding.tb_bidding_document CASCADE;
+CREATE TABLE bidding.tb_bidding_document (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ doc_id VARCHAR(50) NOT NULL, -- 文档ID
+ project_id VARCHAR(50) NOT NULL, -- 所属项目ID
+ doc_type VARCHAR(50) NOT NULL, -- 文档类型:tender-招标文件/technical-技术标/commercial-商务标/clarification-澄清文件/other-其他
+ doc_name VARCHAR(500) NOT NULL, -- 文档名称
+ file_id VARCHAR(50), -- 关联文件表ID
+ file_path VARCHAR(500), -- 文件路径
+ file_size BIGINT, -- 文件大小
+ mime_type VARCHAR(100), -- MIME类型
+ version VARCHAR(20) DEFAULT '1.0', -- 版本号
+ language VARCHAR(20) DEFAULT 'zh-CN', -- 语言
+ page_count INTEGER, -- 页数
+ parse_status VARCHAR(30) DEFAULT 'pending', -- 解析状态:pending-待解析/parsing-解析中/completed-已完成/failed-失败
+ parse_result JSONB, -- 解析结果(JSON格式:提取的要素、表格、图纸等)
+ extraction_data JSONB, -- 提取的结构化数据
+ ai_analysis TEXT, -- AI分析结果
+ upload_date timestamptz DEFAULT now(), -- 上传日期
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (doc_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id)
+);
+
+CREATE INDEX idx_doc_project ON bidding.tb_bidding_document(project_id) WHERE deleted = false;
+CREATE INDEX idx_doc_type ON bidding.tb_bidding_document(doc_type) WHERE deleted = false;
+
+COMMENT ON TABLE bidding.tb_bidding_document IS '招标文件表';
+COMMENT ON COLUMN bidding.tb_bidding_document.parse_status IS '解析状态:pending/parsing/completed/failed';
+
+-- 招标要素提取表
+DROP TABLE IF EXISTS bidding.tb_bidding_requirement CASCADE;
+CREATE TABLE bidding.tb_bidding_requirement (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ req_id VARCHAR(50) NOT NULL, -- 要素ID
+ project_id VARCHAR(50) NOT NULL, -- 所属项目ID
+ doc_id VARCHAR(50), -- 来源文档ID
+ req_category VARCHAR(50) NOT NULL, -- 要素类别:commercial-商务要素/technical-技术参数/veto-否决项/qualification-资质要求/delivery-交付要求/payment-付款条件/scoring-评分标准
+ req_name VARCHAR(255) NOT NULL, -- 要素名称
+ req_content TEXT NOT NULL, -- 要素内容
+ req_value VARCHAR(500), -- 要素值
+ is_mandatory BOOLEAN DEFAULT false, -- 是否必填
+ is_veto BOOLEAN DEFAULT false, -- 是否为否决项
+ priority INTEGER DEFAULT 0, -- 优先级
+ extraction_method VARCHAR(30) DEFAULT 'ai', -- 提取方式:ai-AI提取/manual-人工录入
+ confidence_score DECIMAL(5,4), -- 置信度分数(0-1)
+ source_location JSONB, -- 来源位置(页码、段落等)
+ compliance_status VARCHAR(30), -- 合规状态:compliant-符合/non_compliant-不符合/pending-待确认
+ response_content TEXT, -- 响应内容(我方的应答)
+ notes TEXT, -- 备注
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (req_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id)
+);
+
+CREATE INDEX idx_req_project ON bidding.tb_bidding_requirement(project_id) WHERE deleted = false;
+CREATE INDEX idx_req_category ON bidding.tb_bidding_requirement(req_category) WHERE deleted = false;
+CREATE INDEX idx_req_veto ON bidding.tb_bidding_requirement(is_veto) WHERE deleted = false AND is_veto = true;
+
+COMMENT ON TABLE bidding.tb_bidding_requirement IS '招标要素提取表';
+COMMENT ON COLUMN bidding.tb_bidding_requirement.req_category IS '要素类别:commercial/technical/veto/qualification/delivery/payment/scoring';
+
+-- 投标文件生成表
+DROP TABLE IF EXISTS bidding.tb_bid_response CASCADE;
+CREATE TABLE bidding.tb_bid_response (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ response_id VARCHAR(50) NOT NULL, -- 响应文件ID
+ project_id VARCHAR(50) NOT NULL, -- 所属项目ID
+ response_type VARCHAR(50) NOT NULL, -- 响应类型:technical-技术标/commercial-商务标/comprehensive-综合标
+ doc_name VARCHAR(500) NOT NULL, -- 文档名称
+ outline TEXT, -- 文档大纲(JSON格式)
+ content TEXT, -- 文档内容
+ generation_method VARCHAR(30) DEFAULT 'ai', -- 生成方式:ai-AI生成/template-模板生成/manual-人工编写
+ template_id VARCHAR(50), -- 使用的模板ID
+ ai_model VARCHAR(100), -- 使用的AI模型
+ generation_status VARCHAR(30) DEFAULT 'draft', -- 生成状态:draft-草稿/reviewing-审核中/approved-已批准/rejected-已拒绝/submitted-已提交
+ file_id VARCHAR(50), -- 生成的文件ID
+ file_path VARCHAR(500), -- 文件路径
+ version VARCHAR(20) DEFAULT '1.0', -- 版本号
+ parent_version_id VARCHAR(50), -- 父版本ID
+ review_comments TEXT, -- 审核意见
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (response_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id)
+);
+
+CREATE INDEX idx_response_project ON bidding.tb_bid_response(project_id) WHERE deleted = false;
+CREATE INDEX idx_response_status ON bidding.tb_bid_response(generation_status) WHERE deleted = false;
+
+COMMENT ON TABLE bidding.tb_bid_response IS '投标文件生成表';
+COMMENT ON COLUMN bidding.tb_bid_response.generation_status IS '生成状态:draft/reviewing/approved/rejected/submitted';
+
+-- 评分规则表
+DROP TABLE IF EXISTS bidding.tb_bidding_scoring_rule CASCADE;
+CREATE TABLE bidding.tb_bidding_scoring_rule (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ rule_id VARCHAR(50) NOT NULL, -- 规则ID
+ project_id VARCHAR(50) NOT NULL, -- 所属项目ID
+ rule_category VARCHAR(50) NOT NULL, -- 规则类别:technical-技术分/commercial-商务分/price-价格分/credit-信誉分
+ rule_name VARCHAR(255) NOT NULL, -- 规则名称
+ rule_description TEXT, -- 规则描述
+ max_score DECIMAL(10,2) NOT NULL, -- 最高分值
+ weight DECIMAL(5,4), -- 权重(0-1)
+ scoring_method VARCHAR(50), -- 评分方法:fixed-固定分值/range-区间评分/formula-公式计算
+ calculation_formula TEXT, -- 计算公式
+ evaluation_criteria TEXT, -- 评分标准
+ our_score DECIMAL(10,2), -- 我方得分(预估)
+ score_analysis TEXT, -- 得分分析
+ optimization_advice TEXT, -- 优化建议
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (rule_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id)
+);
+
+CREATE INDEX idx_rule_project ON bidding.tb_bidding_scoring_rule(project_id) WHERE deleted = false;
+
+COMMENT ON TABLE bidding.tb_bidding_scoring_rule IS '评分规则表';
+
+-- 项目流程节点表
+DROP TABLE IF EXISTS bidding.tb_bidding_process CASCADE;
+CREATE TABLE bidding.tb_bidding_process (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ process_id VARCHAR(50) NOT NULL, -- 流程节点ID
+ project_id VARCHAR(50) NOT NULL, -- 所属项目ID
+ node_name VARCHAR(255) NOT NULL, -- 节点名称
+ node_type VARCHAR(50) NOT NULL, -- 节点类型:collection-文件收集/analysis-需求分析/preparation-文件准备/review-内部审核/submission-投标提交/opening-开标/result-结果通知
+ node_order INTEGER NOT NULL, -- 节点顺序
+ node_status VARCHAR(30) DEFAULT 'pending', -- 节点状态:pending-待处理/in_progress-进行中/completed-已完成/skipped-已跳过
+ planned_start_time timestamptz, -- 计划开始时间
+ planned_end_time timestamptz, -- 计划结束时间
+ actual_start_time timestamptz, -- 实际开始时间
+ actual_end_time timestamptz, -- 实际结束时间
+ responsible_user VARCHAR(50), -- 负责人
+ participants VARCHAR(50)[], -- 参与人员数组
+ notes TEXT, -- 节点备注
+ attachments VARCHAR(50)[], -- 附件ID数组
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (process_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id)
+);
+
+CREATE INDEX idx_process_project ON bidding.tb_bidding_process(project_id, node_order) WHERE deleted = false;
+
+COMMENT ON TABLE bidding.tb_bidding_process IS '项目流程节点表';
+COMMENT ON COLUMN bidding.tb_bidding_process.node_type IS '节点类型:collection/analysis/preparation/review/submission/opening/result';
+
+-- 投标模板表
+DROP TABLE IF EXISTS bidding.tb_bid_template CASCADE;
+CREATE TABLE bidding.tb_bid_template (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ template_id VARCHAR(50) NOT NULL, -- 模板ID
+ template_name VARCHAR(255) NOT NULL, -- 模板名称
+ template_type VARCHAR(50) NOT NULL, -- 模板类型:technical-技术标/commercial-商务标/comprehensive-综合标
+ industry VARCHAR(100), -- 适用行业
+ template_content TEXT, -- 模板内容
+ outline_structure JSONB, -- 大纲结构(JSON格式)
+ file_id VARCHAR(50), -- 模板文件ID
+ usage_count INTEGER DEFAULT 0, -- 使用次数
+ is_default BOOLEAN DEFAULT false, -- 是否默认模板
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ status VARCHAR(20) DEFAULT 'active', -- 状态:active-激活/inactive-停用
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (template_id),
+ UNIQUE (optsn)
+);
+
+CREATE INDEX idx_template_type ON bidding.tb_bid_template(template_type) WHERE deleted = false;
+
+COMMENT ON TABLE bidding.tb_bid_template IS '投标模板表';
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableConfig.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableConfig.sql
new file mode 100644
index 0000000..4f06426
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableConfig.sql
@@ -0,0 +1,51 @@
+CREATE SCHEMA IF NOT EXISTS config;
+DROP TABLE IF EXISTS config.tb_sys_config CASCADE;
+CREATE TABLE config.tb_sys_config (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ config_id VARCHAR(50) NOT NULL, -- 配置ID
+ key VARCHAR(255) NOT NULL, -- 配置键
+ name VARCHAR(255) NOT NULL, -- 配置名称
+ value VARCHAR(255) NOT NULL, -- 配置值
+ config_type VARCHAR(50) NOT NULL, -- 数据类型(String, Integer, Boolean, Float, Double)
+ render_type VARCHAR(50) NOT NULL, -- 配置渲染类型(select, input, textarea, checkbox, radio, switch)
+ description VARCHAR(255) NOT NULL, -- 配置描述
+ re JSON DEFAULT NULL, -- 正则表达式校验规则
+ options JSON DEFAULT NULL, -- 可选项,render_type为select、checkbox、radio时使用
+ group VARCHAR(255) NOT NULL, -- 配置组
+ module_id VARCHAR(255) NOT NULL, -- 模块id
+ order_num INT NOT NULL, -- 配置顺序
+ status INT NOT NULL DEFAULT 0, -- 配置状态 0:启用 1:禁用
+ remark VARCHAR(255) NOT NULL, -- 配置备注
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径,支持like递归(如/1/2/3/)
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 配置创建时间
+ update_time timestamptz DEFAULT NULL, -- 配置更新时间
+ delete_time timestamptz DEFAULT NULL, -- 配置删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (config_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE config.tb_sys_config IS '系统配置表';
+COMMENT ON COLUMN config.tb_sys_config.optsn IS '流水号';
+COMMENT ON COLUMN config.tb_sys_config.config_id IS '配置ID';
+COMMENT ON COLUMN config.tb_sys_config.key IS '配置键';
+COMMENT ON COLUMN config.tb_sys_config.name IS '配置名称';
+COMMENT ON COLUMN config.tb_sys_config.value IS '配置值';
+COMMENT ON COLUMN config.tb_sys_config.config_type IS '数据类型';
+COMMENT ON COLUMN config.tb_sys_config.render_type IS '数据渲染类型';
+COMMENT ON COLUMN config.tb_sys_config.description IS '配置描述';
+COMMENT ON COLUMN config.tb_sys_config.re IS '正则表达式校验规则';
+COMMENT ON COLUMN config.tb_sys_config.options IS '可选项';
+COMMENT ON COLUMN config.tb_sys_config.group IS'配置组名称';
+COMMENT ON COLUMN config.tb_sys_config.module_id IS '模块id';
+COMMENT ON COLUMN config.tb_sys_config.order_num IS '配置顺序';
+COMMENT ON COLUMN config.tb_sys_config.status IS '配置状态';
+COMMENT ON COLUMN config.tb_sys_config.remark IS '配置备注';
+COMMENT ON COLUMN config.tb_sys_config.creator IS '创建者';
+COMMENT ON COLUMN config.tb_sys_config.dept_path IS '部门全路径';
+COMMENT ON COLUMN config.tb_sys_config.updater IS '更新者';
+COMMENT ON COLUMN config.tb_sys_config.create_time IS '配置创建时间';
+COMMENT ON COLUMN config.tb_sys_config.update_time IS '配置更新时间';
+COMMENT ON COLUMN config.tb_sys_config.delete_time IS '配置删除时间';
+COMMENT ON COLUMN config.tb_sys_config.deleted IS '是否删除';
\ No newline at end of file
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableCustomerService.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableCustomerService.sql
new file mode 100644
index 0000000..b4a1de1
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableCustomerService.sql
@@ -0,0 +1,289 @@
+-- =============================
+-- 智能客服系统业务模块
+-- 支持:微信小程序客户咨询、智能问答、工单管理、CRM集成
+-- =============================
+CREATE SCHEMA IF NOT EXISTS customer_service;
+
+-- 客户信息表
+DROP TABLE IF EXISTS customer_service.tb_customer CASCADE;
+CREATE TABLE customer_service.tb_customer (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ customer_id VARCHAR(50) NOT NULL, -- 客户ID
+ customer_no VARCHAR(100), -- 客户编号
+ customer_name VARCHAR(255), -- 客户姓名
+ customer_type VARCHAR(30) DEFAULT 'individual', -- 客户类型:individual-个人/enterprise-企业
+ company_name VARCHAR(255), -- 公司名称
+ phone VARCHAR(20), -- 电话
+ email VARCHAR(100), -- 邮箱
+ wechat_openid VARCHAR(100), -- 微信OpenID
+ wechat_unionid VARCHAR(100), -- 微信UnionID
+ avatar VARCHAR(500), -- 头像URL
+ gender INTEGER DEFAULT 0, -- 性别:0-未知/1-男/2-女
+ address VARCHAR(500), -- 地址
+ customer_level VARCHAR(20) DEFAULT 'normal', -- 客户等级:vip/important/normal/potential
+ customer_source VARCHAR(50), -- 客户来源:wechat-微信/web-网站/phone-电话/referral-推荐
+ tags TEXT[], -- 客户标签数组
+ notes TEXT, -- 备注
+ crm_customer_id VARCHAR(50), -- CRM系统客户ID(外部系统)
+ last_contact_time timestamptz, -- 最后联系时间
+ total_consultations INTEGER DEFAULT 0, -- 咨询总次数
+ total_orders INTEGER DEFAULT 0, -- 订单总数
+ total_amount DECIMAL(18,2) DEFAULT 0, -- 总消费金额
+ satisfaction_score DECIMAL(3,2), -- 满意度评分(1-5)
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ status VARCHAR(20) DEFAULT 'active', -- 状态:active-活跃/inactive-非活跃/blacklist-黑名单
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (customer_id),
+ UNIQUE (optsn),
+ UNIQUE (wechat_openid),
+ UNIQUE (phone),
+ UNIQUE (email)
+);
+
+CREATE INDEX idx_customer_type ON customer_service.tb_customer(customer_type) WHERE deleted = false;
+CREATE INDEX idx_customer_level ON customer_service.tb_customer(customer_level) WHERE deleted = false;
+CREATE INDEX idx_customer_wechat ON customer_service.tb_customer(wechat_openid) WHERE deleted = false;
+
+COMMENT ON TABLE customer_service.tb_customer IS '客户信息表';
+COMMENT ON COLUMN customer_service.tb_customer.customer_level IS '客户等级:vip/important/normal/potential';
+
+-- 会话表
+DROP TABLE IF EXISTS customer_service.tb_conversation CASCADE;
+CREATE TABLE customer_service.tb_conversation (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ conversation_id VARCHAR(50) NOT NULL, -- 会话ID
+ customer_id VARCHAR(50) NOT NULL, -- 客户ID
+ conversation_type VARCHAR(30) DEFAULT 'ai', -- 会话类型:ai-AI客服/human-人工客服/transfer-转接
+ channel VARCHAR(20) DEFAULT 'wechat', -- 渠道:wechat-微信/web-网页/app-应用/phone-电话
+ agent_id VARCHAR(50), -- 智能体ID或客服人员ID
+ agent_type VARCHAR(20) DEFAULT 'ai', -- 座席类型:ai-AI/human-人工
+ session_start_time timestamptz DEFAULT now(), -- 会话开始时间
+ session_end_time timestamptz, -- 会话结束时间
+ duration_seconds INTEGER, -- 会话时长(秒)
+ message_count INTEGER DEFAULT 0, -- 消息数量
+ conversation_status VARCHAR(20) DEFAULT 'active', -- 会话状态:active-进行中/closed-已结束/transferred-已转接/timeout-超时
+ satisfaction_rating INTEGER, -- 满意度评分(1-5星)
+ satisfaction_feedback TEXT, -- 满意度反馈
+ summary TEXT, -- 会话摘要(AI生成)
+ tags TEXT[], -- 会话标签
+ metadata JSONB, -- 会话元数据
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (conversation_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (customer_id) REFERENCES customer_service.tb_customer(customer_id)
+);
+
+CREATE INDEX idx_conv_customer ON customer_service.tb_conversation(customer_id, session_start_time DESC) WHERE deleted = false;
+CREATE INDEX idx_conv_status ON customer_service.tb_conversation(conversation_status) WHERE deleted = false;
+CREATE INDEX idx_conv_agent ON customer_service.tb_conversation(agent_id) WHERE deleted = false;
+
+COMMENT ON TABLE customer_service.tb_conversation IS '会话表';
+COMMENT ON COLUMN customer_service.tb_conversation.conversation_type IS '会话类型:ai/human/transfer';
+
+-- 会话消息表
+DROP TABLE IF EXISTS customer_service.tb_conversation_message CASCADE;
+CREATE TABLE customer_service.tb_conversation_message (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ message_id VARCHAR(50) NOT NULL, -- 消息ID
+ conversation_id VARCHAR(50) NOT NULL, -- 所属会话ID
+ sender_type VARCHAR(20) NOT NULL, -- 发送者类型:customer-客户/agent-座席/system-系统
+ sender_id VARCHAR(50), -- 发送者ID
+ message_type VARCHAR(30) NOT NULL DEFAULT 'text',-- 消息类型:text-文本/image-图片/voice-语音/video-视频/file-文件/card-卡片
+ content TEXT, -- 消息内容
+ content_url VARCHAR(500), -- 内容URL(图片、文件等)
+ is_ai_generated BOOLEAN DEFAULT false, -- 是否AI生成
+ ai_model VARCHAR(100), -- 使用的AI模型
+ kb_references VARCHAR(50)[], -- 引用的知识库文档ID数组
+ confidence_score DECIMAL(5,4), -- AI回答置信度
+ sentiment VARCHAR(20), -- 情感分析:positive-正面/neutral-中性/negative-负面
+ intent VARCHAR(100), -- 意图识别结果
+ is_sensitive BOOLEAN DEFAULT false, -- 是否敏感信息
+ read_status BOOLEAN DEFAULT false, -- 已读状态
+ read_time timestamptz, -- 阅读时间
+ metadata JSONB, -- 消息元数据
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间(发送时间)
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (message_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (conversation_id) REFERENCES customer_service.tb_conversation(conversation_id)
+);
+
+CREATE INDEX idx_msg_conversation ON customer_service.tb_conversation_message(conversation_id, create_time) WHERE deleted = false;
+CREATE INDEX idx_msg_sender ON customer_service.tb_conversation_message(sender_id) WHERE deleted = false;
+
+COMMENT ON TABLE customer_service.tb_conversation_message IS '会话消息表';
+COMMENT ON COLUMN customer_service.tb_conversation_message.sentiment IS '情感分析:positive/neutral/negative';
+
+-- 工单表
+DROP TABLE IF EXISTS customer_service.tb_ticket CASCADE;
+CREATE TABLE customer_service.tb_ticket (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ ticket_id VARCHAR(50) NOT NULL, -- 工单ID
+ ticket_no VARCHAR(100) NOT NULL, -- 工单编号
+ customer_id VARCHAR(50) NOT NULL, -- 客户ID
+ conversation_id VARCHAR(50), -- 关联会话ID
+ ticket_type VARCHAR(50) NOT NULL, -- 工单类型:consultation-咨询/complaint-投诉/suggestion-建议/repair-维修/installation-安装/other-其他
+ ticket_category VARCHAR(100), -- 工单分类(具体业务分类)
+ priority VARCHAR(20) DEFAULT 'normal', -- 优先级:urgent-紧急/high-高/normal-普通/low-低
+ title VARCHAR(500) NOT NULL, -- 工单标题
+ description TEXT NOT NULL, -- 问题描述
+ attachments VARCHAR(50)[], -- 附件ID数组
+ ticket_source VARCHAR(30) DEFAULT 'ai', -- 工单来源:ai-AI生成/manual-人工创建/system-系统自动
+ assigned_to VARCHAR(50), -- 分配给(处理人)
+ assigned_dept VARCHAR(50), -- 分配部门
+ ticket_status VARCHAR(30) DEFAULT 'pending', -- 工单状态:pending-待处理/processing-处理中/resolved-已解决/closed-已关闭/cancelled-已取消
+ resolution TEXT, -- 解决方案
+ resolution_time timestamptz, -- 解决时间
+ close_time timestamptz, -- 关闭时间
+ response_time timestamptz, -- 首次响应时间
+ sla_deadline timestamptz, -- SLA截止时间
+ is_overdue BOOLEAN DEFAULT false, -- 是否逾期
+ customer_rating INTEGER, -- 客户评分(1-5星)
+ customer_feedback TEXT, -- 客户反馈
+ crm_ticket_id VARCHAR(50), -- CRM系统工单ID(外部系统)
+ sync_status VARCHAR(20) DEFAULT 'pending', -- 同步状态:pending-待同步/synced-已同步/failed-失败
+ tags TEXT[], -- 工单标签
+ metadata JSONB, -- 工单元数据
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (ticket_id),
+ UNIQUE (optsn),
+ UNIQUE (ticket_no),
+ FOREIGN KEY (customer_id) REFERENCES customer_service.tb_customer(customer_id)
+);
+
+CREATE INDEX idx_ticket_customer ON customer_service.tb_ticket(customer_id) WHERE deleted = false;
+CREATE INDEX idx_ticket_status ON customer_service.tb_ticket(ticket_status) WHERE deleted = false;
+CREATE INDEX idx_ticket_assigned ON customer_service.tb_ticket(assigned_to) WHERE deleted = false;
+CREATE INDEX idx_ticket_priority ON customer_service.tb_ticket(priority) WHERE deleted = false;
+CREATE INDEX idx_ticket_sla ON customer_service.tb_ticket(sla_deadline) WHERE deleted = false AND is_overdue = false;
+
+COMMENT ON TABLE customer_service.tb_ticket IS '工单表';
+COMMENT ON COLUMN customer_service.tb_ticket.ticket_type IS '工单类型:consultation/complaint/suggestion/repair/installation/other';
+
+-- 工单处理记录表
+DROP TABLE IF EXISTS customer_service.tb_ticket_log CASCADE;
+CREATE TABLE customer_service.tb_ticket_log (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ log_id VARCHAR(50) NOT NULL, -- 日志ID
+ ticket_id VARCHAR(50) NOT NULL, -- 工单ID
+ action_type VARCHAR(50) NOT NULL, -- 操作类型:create-创建/assign-分配/update-更新/comment-评论/resolve-解决/close-关闭/reopen-重开
+ action_content TEXT, -- 操作内容
+ old_value TEXT, -- 旧值
+ new_value TEXT, -- 新值
+ operator_id VARCHAR(50), -- 操作人ID
+ operator_name VARCHAR(100), -- 操作人姓名
+ attachments VARCHAR(50)[], -- 附件ID数组
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ PRIMARY KEY (log_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (ticket_id) REFERENCES customer_service.tb_ticket(ticket_id)
+);
+
+CREATE INDEX idx_ticket_log_ticket ON customer_service.tb_ticket_log(ticket_id, create_time DESC);
+
+COMMENT ON TABLE customer_service.tb_ticket_log IS '工单处理记录表';
+
+-- FAQ表(常见问题)
+DROP TABLE IF EXISTS customer_service.tb_faq CASCADE;
+CREATE TABLE customer_service.tb_faq (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ faq_id VARCHAR(50) NOT NULL, -- FAQ ID
+ kb_id VARCHAR(50), -- 关联知识库ID
+ category VARCHAR(100) NOT NULL, -- 分类
+ question TEXT NOT NULL, -- 问题
+ answer TEXT NOT NULL, -- 答案
+ similar_questions TEXT[], -- 相似问题数组
+ keywords TEXT[], -- 关键词数组
+ hit_count INTEGER DEFAULT 0, -- 命中次数
+ helpful_count INTEGER DEFAULT 0, -- 有帮助次数
+ unhelpful_count INTEGER DEFAULT 0, -- 无帮助次数
+ is_published BOOLEAN DEFAULT false, -- 是否发布
+ priority INTEGER DEFAULT 0, -- 优先级
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (faq_id),
+ UNIQUE (optsn)
+);
+
+CREATE INDEX idx_faq_category ON customer_service.tb_faq(category) WHERE deleted = false;
+CREATE INDEX idx_faq_published ON customer_service.tb_faq(is_published) WHERE deleted = false AND is_published = true;
+
+COMMENT ON TABLE customer_service.tb_faq IS 'FAQ常见问题表';
+
+-- 客服评价表
+DROP TABLE IF EXISTS customer_service.tb_service_evaluation CASCADE;
+CREATE TABLE customer_service.tb_service_evaluation (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ evaluation_id VARCHAR(50) NOT NULL, -- 评价ID
+ customer_id VARCHAR(50) NOT NULL, -- 客户ID
+ conversation_id VARCHAR(50), -- 会话ID
+ ticket_id VARCHAR(50), -- 工单ID
+ evaluation_type VARCHAR(30) NOT NULL, -- 评价类型:conversation-会话/ticket-工单/overall-整体服务
+ rating INTEGER NOT NULL, -- 评分(1-5星)
+ dimensions JSONB, -- 分维度评分(JSON格式:响应速度、专业性、态度等)
+ feedback TEXT, -- 评价反馈
+ tags TEXT[], -- 评价标签
+ is_anonymous BOOLEAN DEFAULT false, -- 是否匿名
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (evaluation_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (customer_id) REFERENCES customer_service.tb_customer(customer_id)
+);
+
+CREATE INDEX idx_eval_customer ON customer_service.tb_service_evaluation(customer_id) WHERE deleted = false;
+CREATE INDEX idx_eval_rating ON customer_service.tb_service_evaluation(rating) WHERE deleted = false;
+
+COMMENT ON TABLE customer_service.tb_service_evaluation IS '客服评价表';
+
+-- CRM集成配置表
+DROP TABLE IF EXISTS customer_service.tb_crm_config CASCADE;
+CREATE TABLE customer_service.tb_crm_config (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ config_id VARCHAR(50) NOT NULL, -- 配置ID
+ crm_system VARCHAR(50) NOT NULL, -- CRM系统名称
+ api_endpoint VARCHAR(500) NOT NULL, -- API端点
+ api_key VARCHAR(500), -- API密钥(加密存储)
+ auth_type VARCHAR(30) DEFAULT 'api_key', -- 认证类型:api_key/oauth2/basic_auth
+ sync_interval INTEGER DEFAULT 3600, -- 同步间隔(秒)
+ sync_direction VARCHAR(30) DEFAULT 'bidirectional',-- 同步方向:to_crm-单向到CRM/from_crm-单向从CRM/bidirectional-双向
+ field_mapping JSONB, -- 字段映射配置
+ sync_enabled BOOLEAN DEFAULT false, -- 是否启用同步
+ last_sync_time timestamptz, -- 最后同步时间
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (config_id),
+ UNIQUE (optsn)
+);
+
+COMMENT ON TABLE customer_service.tb_crm_config IS 'CRM集成配置表';
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableFile.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableFile.sql
new file mode 100644
index 0000000..f3f38da
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableFile.sql
@@ -0,0 +1,42 @@
+CREATE SCHEMA IF NOT EXISTS file;
+
+DROP TABLE IF EXISTS file.tb_sys_file CASCADE;
+CREATE TABLE file.tb_sys_file (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ file_id VARCHAR(50) NOT NULL, -- 文件ID
+ name VARCHAR(255) NOT NULL, -- 文件名
+ path VARCHAR(255) NOT NULL, -- 文件路径
+ size BIGINT NOT NULL, -- 文件大小
+ type VARCHAR(50) NOT NULL, -- 文件类型
+ storage_type VARCHAR(50) NOT NULL, -- 存储类型
+ mime_type VARCHAR(255) NOT NULL, -- 文件MIME类型
+ url VARCHAR(255) NOT NULL, -- 文件URL
+ status VARCHAR(50) NOT NULL, -- 文件状态
+ dept_path VARCHAR(255) NOT NULL, -- 当前部门路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (file_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE file.tb_sys_file IS '文件表';
+COMMENT ON COLUMN file.tb_sys_file.optsn IS '流水号';
+COMMENT ON COLUMN file.tb_sys_file.file_id IS '文件ID';
+COMMENT ON COLUMN file.tb_sys_file.name IS '文件名';
+COMMENT ON COLUMN file.tb_sys_file.path IS '文件路径';
+COMMENT ON COLUMN file.tb_sys_file.size IS '文件大小';
+COMMENT ON COLUMN file.tb_sys_file.type IS '文件类型';
+COMMENT ON COLUMN file.tb_sys_file.storage_type IS '存储类型';
+COMMENT ON COLUMN file.tb_sys_file.mime_type IS '文件MIME类型';
+COMMENT ON COLUMN file.tb_sys_file.url IS '文件URL';
+COMMENT ON COLUMN file.tb_sys_file.status IS '文件状态';
+COMMENT ON COLUMN file.tb_sys_file.dept_path IS '当前部门路径';
+COMMENT ON COLUMN file.tb_sys_file.creator IS '创建者';
+COMMENT ON COLUMN file.tb_sys_file.updater IS '更新者';
+COMMENT ON COLUMN file.tb_sys_file.create_time IS '创建时间';
+COMMENT ON COLUMN file.tb_sys_file.update_time IS '更新时间';
+COMMENT ON COLUMN file.tb_sys_file.delete_time IS '删除时间';
+COMMENT ON COLUMN file.tb_sys_file.deleted IS '是否删除';
\ No newline at end of file
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableKnowledge.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableKnowledge.sql
new file mode 100644
index 0000000..bcafe1e
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableKnowledge.sql
@@ -0,0 +1,138 @@
+-- =============================
+-- 知识库管理模块
+-- 支持:招投标知识库、客服知识库、企业内部知识库
+-- =============================
+CREATE SCHEMA IF NOT EXISTS knowledge;
+
+-- 知识库表(多租户知识库定义)
+DROP TABLE IF EXISTS knowledge.tb_knowledge_base CASCADE;
+CREATE TABLE knowledge.tb_knowledge_base (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ kb_id VARCHAR(50) NOT NULL, -- 知识库ID
+ name VARCHAR(255) NOT NULL, -- 知识库名称
+ kb_type VARCHAR(50) NOT NULL, -- 知识库类型:bidding-招投标/customer_service-客服/internal-内部协同
+ access_level VARCHAR(20) NOT NULL DEFAULT 'private', -- 访问级别:public-公开/private-私有/internal-内部
+ description TEXT, -- 知识库描述
+ storage_path VARCHAR(500), -- 存储路径
+ version VARCHAR(20) DEFAULT '1.0', -- 当前版本号
+ config JSONB DEFAULT NULL, -- 知识库配置(JSON格式:索引配置、检索参数等)
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态:active-激活/inactive-停用/archived-归档
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (kb_id),
+ UNIQUE (optsn)
+);
+
+CREATE INDEX idx_kb_type ON knowledge.tb_knowledge_base(kb_type) WHERE deleted = false;
+CREATE INDEX idx_kb_dept_path ON knowledge.tb_knowledge_base(dept_path) WHERE deleted = false;
+
+COMMENT ON TABLE knowledge.tb_knowledge_base IS '知识库表';
+COMMENT ON COLUMN knowledge.tb_knowledge_base.kb_type IS '知识库类型:bidding-招投标/customer_service-客服/internal-内部协同';
+COMMENT ON COLUMN knowledge.tb_knowledge_base.access_level IS '访问级别:public-公开/private-私有/internal-内部';
+
+-- 知识文档表
+DROP TABLE IF EXISTS knowledge.tb_knowledge_document CASCADE;
+CREATE TABLE knowledge.tb_knowledge_document (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ doc_id VARCHAR(50) NOT NULL, -- 文档ID
+ kb_id VARCHAR(50) NOT NULL, -- 所属知识库ID
+ title VARCHAR(500) NOT NULL, -- 文档标题
+ doc_type VARCHAR(50) NOT NULL, -- 文档类型:text-文本/pdf/word/excel/image/video
+ category VARCHAR(100), -- 文档分类(自动或手动分类)
+ content TEXT, -- 文档内容(文本类型)
+ content_summary TEXT, -- 内容摘要(AI生成)
+ file_id VARCHAR(50), -- 关联文件表ID
+ file_path VARCHAR(500), -- 文件路径
+ file_size BIGINT, -- 文件大小(字节)
+ mime_type VARCHAR(100), -- MIME类型
+ version VARCHAR(20) DEFAULT '1.0', -- 文档版本号
+ parent_doc_id VARCHAR(50), -- 父文档ID(用于版本管理)
+ tags TEXT[], -- 文档标签数组
+ keywords TEXT[], -- 关键词数组(AI提取)
+ embedding_status VARCHAR(20) DEFAULT 'pending', -- 向量化状态:pending-待处理/processing-处理中/completed-完成/failed-失败
+ embedding_model VARCHAR(100), -- 使用的向量化模型
+ chunk_count INTEGER DEFAULT 0, -- 切片数量
+ metadata JSONB DEFAULT NULL, -- 文档元数据(JSON格式)
+ source_url VARCHAR(500), -- 来源URL
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态:active-激活/inactive-停用/archived-归档
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (doc_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (kb_id) REFERENCES knowledge.tb_knowledge_base(kb_id)
+);
+
+CREATE INDEX idx_doc_kb ON knowledge.tb_knowledge_document(kb_id) WHERE deleted = false;
+CREATE INDEX idx_doc_category ON knowledge.tb_knowledge_document(category) WHERE deleted = false;
+CREATE INDEX idx_doc_embedding_status ON knowledge.tb_knowledge_document(embedding_status) WHERE deleted = false;
+CREATE INDEX idx_doc_tags ON knowledge.tb_knowledge_document USING GIN(tags) WHERE deleted = false;
+
+COMMENT ON TABLE knowledge.tb_knowledge_document IS '知识文档表';
+COMMENT ON COLUMN knowledge.tb_knowledge_document.embedding_status IS '向量化状态:pending/processing/completed/failed';
+
+-- 知识文档片段表(用于RAG检索)
+DROP TABLE IF EXISTS knowledge.tb_knowledge_chunk CASCADE;
+CREATE TABLE knowledge.tb_knowledge_chunk (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ chunk_id VARCHAR(50) NOT NULL, -- 片段ID
+ doc_id VARCHAR(50) NOT NULL, -- 所属文档ID
+ kb_id VARCHAR(50) NOT NULL, -- 所属知识库ID
+ chunk_index INTEGER NOT NULL, -- 片段索引(在文档中的顺序)
+ content TEXT NOT NULL, -- 片段内容
+ content_length INTEGER, -- 内容长度
+ embedding vector(1536), -- 向量嵌入(假设使用OpenAI 1536维)
+ chunk_type VARCHAR(20) DEFAULT 'text', -- 片段类型:text-文本/table-表格/image-图片
+ position_info JSONB, -- 位置信息(页码、坐标等)
+ metadata JSONB, -- 片段元数据
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (chunk_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (doc_id) REFERENCES knowledge.tb_knowledge_document(doc_id),
+ FOREIGN KEY (kb_id) REFERENCES knowledge.tb_knowledge_base(kb_id)
+);
+
+CREATE INDEX idx_chunk_doc ON knowledge.tb_knowledge_chunk(doc_id) WHERE deleted = false;
+CREATE INDEX idx_chunk_kb ON knowledge.tb_knowledge_chunk(kb_id) WHERE deleted = false;
+-- 向量检索索引(需要安装pgvector扩展)
+-- CREATE INDEX idx_chunk_embedding ON knowledge.tb_knowledge_chunk USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
+
+COMMENT ON TABLE knowledge.tb_knowledge_chunk IS '知识文档片段表(RAG检索)';
+COMMENT ON COLUMN knowledge.tb_knowledge_chunk.embedding IS '向量嵌入(需要pgvector扩展)';
+
+-- 知识访问日志表
+DROP TABLE IF EXISTS knowledge.tb_knowledge_access_log CASCADE;
+CREATE TABLE knowledge.tb_knowledge_access_log (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ log_id VARCHAR(50) NOT NULL, -- 日志ID
+ kb_id VARCHAR(50), -- 知识库ID
+ doc_id VARCHAR(50), -- 文档ID
+ user_id VARCHAR(50) NOT NULL, -- 用户ID
+ access_type VARCHAR(20) NOT NULL, -- 访问类型:view-查看/download-下载/search-搜索/edit-编辑
+ query_text TEXT, -- 搜索查询文本
+ result_count INTEGER, -- 搜索结果数量
+ ip_address VARCHAR(45), -- IP地址
+ user_agent TEXT, -- 用户代理
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ PRIMARY KEY (log_id),
+ UNIQUE (optsn)
+);
+
+CREATE INDEX idx_access_log_user ON knowledge.tb_knowledge_access_log(user_id, create_time DESC);
+CREATE INDEX idx_access_log_kb ON knowledge.tb_knowledge_access_log(kb_id, create_time DESC);
+
+COMMENT ON TABLE knowledge.tb_knowledge_access_log IS '知识访问日志表';
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableLog.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableLog.sql
new file mode 100644
index 0000000..524accd
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableLog.sql
@@ -0,0 +1,39 @@
+CREATE SCHEMA IF NOT EXISTS log;
+DROP TABLE IF EXISTS log.tb_sys_log CASCADE;
+CREATE TABLE log.tb_sys_log (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ log_id VARCHAR(50) NOT NULL, -- 日志ID
+ type VARCHAR(50) NOT NULL, -- 日志类型
+ level VARCHAR(50) NOT NULL, -- 日志级别
+ module VARCHAR(50) NOT NULL, -- 日志模块
+ ip_address varchar(45), -- IP地址
+ ip_source varchar(100), -- IP来源
+ browser varchar(100), -- 浏览器
+ os varchar(100), -- 操作系统
+ message VARCHAR(255) NOT NULL, -- 日志消息
+ data JSONB DEFAULT NULL, -- 日志数据
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 日志创建时间
+ update_time timestamptz DEFAULT NULL, -- 日志更新时间
+ delete_time timestamptz DEFAULT NULL, -- 日志删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (log_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE log.tb_sys_log IS '系统日志表';
+COMMENT ON COLUMN log.tb_sys_log.optsn IS '流水号';
+COMMENT ON COLUMN log.tb_sys_log.log_id IS '日志ID';
+COMMENT ON COLUMN log.tb_sys_log.type IS '日志类型';
+COMMENT ON COLUMN log.tb_sys_log.level IS '日志级别';
+COMMENT ON COLUMN log.tb_sys_log.module IS '日志模块';
+COMMENT ON COLUMN log.tb_sys_log.message IS '日志消息';
+COMMENT ON COLUMN log.tb_sys_log.data IS '日志数据';
+COMMENT ON COLUMN log.tb_sys_log.creator IS '创建者';
+COMMENT ON COLUMN log.tb_sys_log.dept_path IS '部门全路径';
+COMMENT ON COLUMN log.tb_sys_log.updater IS '更新者';
+COMMENT ON COLUMN log.tb_sys_log.create_time IS '日志创建时间';
+COMMENT ON COLUMN log.tb_sys_log.update_time IS '日志更新时间';
+COMMENT ON COLUMN log.tb_sys_log.delete_time IS '日志删除时间';
+COMMENT ON COLUMN log.tb_sys_log.deleted IS '是否删除';
\ No newline at end of file
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableMessage.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableMessage.sql
new file mode 100644
index 0000000..41a42a1
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableMessage.sql
@@ -0,0 +1,161 @@
+CREATE SCHEMA IF NOT EXISTS message;
+
+DROP TABLE IF EXISTS message.tb_message CASCADE;
+CREATE TABLE message.tb_message (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ message_id VARCHAR(50) NOT NULL, -- 消息ID
+ title VARCHAR(255) NOT NULL, -- 消息标题
+ content VARCHAR(255) NOT NULL, -- 消息内容
+ type VARCHAR(50) NOT NULL, -- 消息类型
+ status VARCHAR(50) NOT NULL, -- 消息状态
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(隔离)
+ creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL,
+ delete_time timestamptz DEFAULT NULL,
+ deleted BOOLEAN NOT NULL DEFAULT false,
+ PRIMARY KEY (message_id),
+ UNIQUE (optsn)
+);
+
+COMMENT ON TABLE message.tb_message IS '消息表';
+COMMENT ON COLUMN message.tb_message.optsn IS '流水号';
+COMMENT ON COLUMN message.tb_message.message_id IS '消息ID';
+COMMENT ON COLUMN message.tb_message.title IS '消息标题';
+COMMENT ON COLUMN message.tb_message.content IS '消息内容';
+COMMENT ON COLUMN message.tb_message.type IS '消息类型';
+COMMENT ON COLUMN message.tb_message.status IS '消息状态';
+COMMENT ON COLUMN message.tb_message.dept_path IS '部门全路径';
+COMMENT ON COLUMN message.tb_message.creator IS '创建者';
+COMMENT ON COLUMN message.tb_message.updater IS '更新者';
+COMMENT ON COLUMN message.tb_message.create_time IS '创建时间';
+COMMENT ON COLUMN message.tb_message.update_time IS '更新时间';
+COMMENT ON COLUMN message.tb_message.delete_time IS '删除时间';
+COMMENT ON COLUMN message.tb_message.deleted IS '是否删除';
+
+
+-- 消息发送范围定义表(定义消息要发送给哪些对象,通过什么渠道)
+DROP TABLE IF EXISTS message.tb_message_range CASCADE;
+CREATE TABLE message.tb_message_range (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ message_id VARCHAR(50) NOT NULL, -- 消息ID
+ target_type VARCHAR(20) NOT NULL, -- 目标类型:user/dept/role/all
+ target_id VARCHAR(50) DEFAULT NULL, -- 目标ID(用户、部门、角色ID等,all类型时为空)
+ channel VARCHAR(20) NOT NULL DEFAULT 'app', -- 发送渠道:app/sms/email/wechat等
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径,支持like递归(如/1/2/3/)
+ creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (optsn),
+ UNIQUE (message_id, target_type, target_id, channel)
+);
+
+COMMENT ON TABLE message.tb_message_range IS '消息发送范围定义表';
+COMMENT ON COLUMN message.tb_message_range.optsn IS '流水号';
+COMMENT ON COLUMN message.tb_message_range.message_id IS '消息ID';
+COMMENT ON COLUMN message.tb_message_range.target_type IS '目标类型:user-指定用户/dept-部门/role-角色/all-全员';
+COMMENT ON COLUMN message.tb_message_range.target_id IS '目标ID(用户、部门、角色ID等,all类型时为空)';
+COMMENT ON COLUMN message.tb_message_range.channel IS '发送渠道:app/sms/email/wechat等';
+COMMENT ON COLUMN message.tb_message_range.dept_path IS '部门全路径';
+COMMENT ON COLUMN message.tb_message_range.creator IS '创建者';
+COMMENT ON COLUMN message.tb_message_range.updater IS '更新者';
+COMMENT ON COLUMN message.tb_message_range.create_time IS '创建时间';
+COMMENT ON COLUMN message.tb_message_range.update_time IS '更新时间';
+COMMENT ON COLUMN message.tb_message_range.delete_time IS '删除时间';
+COMMENT ON COLUMN message.tb_message_range.deleted IS '是否删除';
+
+
+-- 用户消息接收记录表(记录每个用户实际收到的消息及处理状态)
+DROP TABLE IF EXISTS message.tb_message_receiver CASCADE;
+CREATE TABLE message.tb_message_receiver (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ message_id VARCHAR(50) NOT NULL, -- 消息ID
+ user_id VARCHAR(50) NOT NULL, -- 用户ID
+ channel VARCHAR(20) DEFAULT 'app', -- 接收渠道:app/sms/email/wechat等
+ status VARCHAR(20) NOT NULL DEFAULT 'unread', -- 消息状态:unread-未读/read-已读/handled-已处理/deleted-已删除
+ read_time timestamptz DEFAULT NULL, -- 阅读时间
+ handle_time timestamptz DEFAULT NULL, -- 处理时间
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离)
+ creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间(接收时间)
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (optsn),
+ UNIQUE (message_id, user_id, channel)
+);
+
+-- 创建索引以提高查询效率
+CREATE INDEX idx_message_user_user_status ON message.tb_message_receiver(user_id, status, create_time DESC) WHERE deleted = false;
+CREATE INDEX idx_message_user_message ON message.tb_message_receiver(message_id) WHERE deleted = false;
+
+COMMENT ON TABLE message.tb_message_receiver IS '用户消息接收记录表';
+COMMENT ON COLUMN message.tb_message_receiver.optsn IS '流水号';
+COMMENT ON COLUMN message.tb_message_receiver.message_id IS '消息ID';
+COMMENT ON COLUMN message.tb_message_receiver.user_id IS '用户ID';
+COMMENT ON COLUMN message.tb_message_receiver.channel IS '接收渠道:app/sms/email/wechat等';
+COMMENT ON COLUMN message.tb_message_receiver.status IS '消息状态:unread-未读/read-已读/handled-已处理/deleted-已删除';
+COMMENT ON COLUMN message.tb_message_receiver.read_time IS '阅读时间';
+COMMENT ON COLUMN message.tb_message_receiver.handle_time IS '处理时间';
+COMMENT ON COLUMN message.tb_message_receiver.dept_path IS '部门全路径';
+COMMENT ON COLUMN message.tb_message_receiver.creator IS '创建者';
+COMMENT ON COLUMN message.tb_message_receiver.updater IS '更新者';
+COMMENT ON COLUMN message.tb_message_receiver.create_time IS '创建时间(接收时间)';
+COMMENT ON COLUMN message.tb_message_receiver.update_time IS '更新时间';
+COMMENT ON COLUMN message.tb_message_receiver.delete_time IS '删除时间';
+COMMENT ON COLUMN message.tb_message_receiver.deleted IS '是否删除';
+
+
+-- 消息渠道配置表(管理各种消息发送渠道的配置)
+DROP TABLE IF EXISTS message.tb_message_channel CASCADE;
+CREATE TABLE message.tb_message_channel (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ channel_id VARCHAR(50) NOT NULL, -- 渠道ID
+ channel_code VARCHAR(20) NOT NULL, -- 渠道编码:app/sms/email/wechat/dingtalk等
+ channel_name VARCHAR(100) NOT NULL, -- 渠道名称
+ channel_desc VARCHAR(255) DEFAULT NULL, -- 渠道描述
+ config TEXT DEFAULT NULL, -- 渠道配置(JSON格式,如API密钥、服务器地址等)
+ status VARCHAR(20) NOT NULL DEFAULT 'enabled', -- 渠道状态:enabled-启用/disabled-禁用/maintenance-维护中
+ priority INTEGER DEFAULT 0, -- 优先级(数字越大优先级越高)
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离)
+ creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (channel_id),
+ UNIQUE (optsn),
+ UNIQUE (channel_code)
+);
+
+COMMENT ON TABLE message.tb_message_channel IS '消息渠道配置表';
+COMMENT ON COLUMN message.tb_message_channel.optsn IS '流水号';
+COMMENT ON COLUMN message.tb_message_channel.channel_id IS '渠道ID';
+COMMENT ON COLUMN message.tb_message_channel.channel_code IS '渠道编码:app/sms/email/wechat/dingtalk等';
+COMMENT ON COLUMN message.tb_message_channel.channel_name IS '渠道名称';
+COMMENT ON COLUMN message.tb_message_channel.channel_desc IS '渠道描述';
+COMMENT ON COLUMN message.tb_message_channel.config IS '渠道配置(JSON格式)';
+COMMENT ON COLUMN message.tb_message_channel.status IS '渠道状态:enabled-启用/disabled-禁用/maintenance-维护中';
+COMMENT ON COLUMN message.tb_message_channel.priority IS '优先级(数字越大优先级越高)';
+COMMENT ON COLUMN message.tb_message_channel.dept_path IS '部门全路径';
+COMMENT ON COLUMN message.tb_message_channel.creator IS '创建者';
+COMMENT ON COLUMN message.tb_message_channel.updater IS '更新者';
+COMMENT ON COLUMN message.tb_message_channel.create_time IS '创建时间';
+COMMENT ON COLUMN message.tb_message_channel.update_time IS '更新时间';
+COMMENT ON COLUMN message.tb_message_channel.delete_time IS '删除时间';
+COMMENT ON COLUMN message.tb_message_channel.deleted IS '是否删除';
+
+-- 插入默认渠道配置
+INSERT INTO message.tb_message_channel (optsn, channel_id, channel_code, channel_name, channel_desc, status, priority)
+VALUES
+ ('CHANNEL_APP_001', 'CH_APP', 'app', '应用内消息', '系统内部消息推送', 'enabled', 100),
+ ('CHANNEL_SMS_001', 'CH_SMS', 'sms', '短信通知', '手机短信推送', 'disabled', 80),
+ ('CHANNEL_EMAIL_001', 'CH_EMAIL', 'email', '邮件通知', '电子邮件推送', 'disabled', 60),
+ ('CHANNEL_WECHAT_001', 'CH_WECHAT', 'wechat', '微信通知', '微信公众号/企业微信推送', 'disabled', 70),
+ ('CHANNEL_DINGTALK_001', 'CH_DINGTALK', 'dingtalk', '钉钉通知', '钉钉工作通知推送', 'disabled', 70);
\ No newline at end of file
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTablePermission.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTablePermission.sql
new file mode 100644
index 0000000..486363f
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTablePermission.sql
@@ -0,0 +1,461 @@
+CREATE SCHEMA IF NOT EXISTS sys;
+
+-- 通用更新时间触发函数(用于模拟 MySQL 的 ON UPDATE CURRENT_TIMESTAMP)
+-- CREATE OR REPLACE FUNCTION sys.set_update_time()
+-- RETURNS trigger AS $$
+-- BEGIN
+-- NEW.update_time := CURRENT_TIMESTAMP;
+-- RETURN NEW;
+-- END;
+-- $$ LANGUAGE plpgsql;
+
+-- 部门表
+DROP TABLE IF EXISTS sys.tb_sys_dept CASCADE;
+CREATE TABLE sys.tb_sys_dept (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ dept_id VARCHAR(50) NOT NULL, -- 部门ID
+ name VARCHAR(100) NOT NULL, -- 部门名称
+ parent_id VARCHAR(50) DEFAULT NULL, -- 父部门ID
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ description VARCHAR(255) DEFAULT NULL, -- 部门描述
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (dept_id),
+ UNIQUE (optsn)
+);
+-- 创建索引
+CREATE INDEX idx_sys_dept_parent ON sys.tb_sys_dept USING btree (parent_id);
+COMMENT ON TABLE sys.tb_sys_dept IS '部门表';
+COMMENT ON COLUMN sys.tb_sys_dept.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_dept.dept_id IS '部门ID';
+COMMENT ON COLUMN sys.tb_sys_dept.name IS '部门名称';
+COMMENT ON COLUMN sys.tb_sys_dept.parent_id IS '父部门ID';
+COMMENT ON COLUMN sys.tb_sys_dept.dept_path IS '部门全路径';
+COMMENT ON COLUMN sys.tb_sys_dept.description IS '部门描述';
+COMMENT ON COLUMN sys.tb_sys_dept.creator IS '创建者';
+COMMENT ON COLUMN sys.tb_sys_dept.updater IS '更新者';
+COMMENT ON COLUMN sys.tb_sys_dept.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_dept.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_dept.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_dept.deleted IS '是否删除';
+
+-- 角色表
+DROP TABLE IF EXISTS sys.tb_sys_role CASCADE;
+CREATE TABLE sys.tb_sys_role (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ role_id VARCHAR(50) NOT NULL, -- 角色ID
+ name VARCHAR(100) NOT NULL,
+ description VARCHAR(200) DEFAULT NULL, -- 角色名称
+ scope VARCHAR(20) NOT NULL DEFAULT 'dept', -- 角色作用域:global/dept
+ owner_dept_id VARCHAR(50) DEFAULT NULL, -- 当scope=dept时,所属部门ID
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ dept_path VARCHAR(255) DEFAULT NULL,
+ status boolean NOT NULL DEFAULT false, -- 部门全路径
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (role_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE sys.tb_sys_role IS '角色表';
+COMMENT ON COLUMN sys.tb_sys_role.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_role.role_id IS '角色ID';
+COMMENT ON COLUMN sys.tb_sys_role.name IS '角色名称';
+COMMENT ON COLUMN sys.tb_sys_role.description IS '角色名称';
+COMMENT ON COLUMN sys.tb_sys_role.scope IS '角色作用域:global=通用,dept=部门私有';
+COMMENT ON COLUMN sys.tb_sys_role.owner_dept_id IS '部门私有角色的所属部门ID(scope=dept 时必填)';
+COMMENT ON COLUMN sys.tb_sys_role.status IS '角色状态';
+COMMENT ON COLUMN sys.tb_sys_role.creator IS '创建者';
+COMMENT ON COLUMN sys.tb_sys_role.dept_path IS '部门全路径';
+COMMENT ON COLUMN sys.tb_sys_role.updater IS '更新者';
+COMMENT ON COLUMN sys.tb_sys_role.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_role.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_role.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_role.deleted IS '是否删除';
+
+-- 唯一性:
+-- 全局角色:name 在未删除且 scope=global 下唯一
+-- 部门私有角色:同一部门下 name 唯一(scope=dept)
+CREATE UNIQUE INDEX IF NOT EXISTS uq_sys_role_global_name
+ ON sys.tb_sys_role USING btree (name)
+ WHERE deleted = false AND scope = 'global';
+CREATE UNIQUE INDEX IF NOT EXISTS uq_sys_role_dept_name
+ ON sys.tb_sys_role USING btree (owner_dept_id, name)
+ WHERE deleted = false AND scope = 'dept';
+CREATE INDEX IF NOT EXISTS idx_sys_role_owner_dept ON sys.tb_sys_role USING btree (owner_dept_id) WHERE deleted = false;
+
+-- 部门角色关联表
+DROP TABLE IF EXISTS sys.tb_sys_dept_role CASCADE;
+CREATE TABLE sys.tb_sys_dept_role (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ dept_id VARCHAR(50) NOT NULL, -- 部门ID
+ role_id VARCHAR(50) NOT NULL, -- 角色ID
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (dept_id, role_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE sys.tb_sys_dept_role IS '部门角色关联表';
+COMMENT ON COLUMN sys.tb_sys_dept_role.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_dept_role.dept_id IS '部门ID';
+COMMENT ON COLUMN sys.tb_sys_dept_role.role_id IS '角色ID';
+COMMENT ON COLUMN sys.tb_sys_dept_role.dept_path IS '部门全路径';
+COMMENT ON COLUMN sys.tb_sys_dept_role.creator IS '创建者';
+COMMENT ON COLUMN sys.tb_sys_dept_role.updater IS '更新者';
+COMMENT ON COLUMN sys.tb_sys_dept_role.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_dept_role.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_dept_role.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_dept_role.deleted IS '是否删除';
+
+-- 用户角色关联表
+DROP TABLE IF EXISTS sys.tb_sys_user_role CASCADE;
+CREATE TABLE sys.tb_sys_user_role (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ user_id VARCHAR(50) NOT NULL, -- 用户ID
+ role_id VARCHAR(50) NOT NULL, -- 角色ID
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (user_id, role_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE sys.tb_sys_user_role IS '用户角色关联表';
+COMMENT ON COLUMN sys.tb_sys_user_role.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_user_role.user_id IS '用户ID';
+COMMENT ON COLUMN sys.tb_sys_user_role.role_id IS '角色ID';
+COMMENT ON COLUMN sys.tb_sys_user_role.dept_path IS '部门全路径';
+COMMENT ON COLUMN sys.tb_sys_user_role.creator IS '创建者';
+COMMENT ON COLUMN sys.tb_sys_user_role.updater IS '更新者';
+COMMENT ON COLUMN sys.tb_sys_user_role.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_user_role.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_user_role.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_user_role.deleted IS '是否删除';
+
+-- 视图表
+DROP TABLE IF EXISTS sys.tb_sys_view CASCADE;
+CREATE TABLE sys.tb_sys_view (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ view_id VARCHAR(50) NOT NULL, -- 视图ID
+ name VARCHAR(100) NOT NULL, -- 视图名称
+ parent_id VARCHAR(50) DEFAULT NULL, -- 父视图ID
+ url VARCHAR(255) DEFAULT NULL, -- 视图URL
+ component VARCHAR(255) DEFAULT NULL, -- 视图组件
+ icon VARCHAR(100) DEFAULT NULL, -- 视图图标
+ type integer DEFAULT 0, -- 视图类型
+ layout VARCHAR(100) DEFAULT NULL, -- 布局组件路径名称
+ order_num integer DEFAULT 0, -- 视图排序号
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ description VARCHAR(255) DEFAULT NULL, -- 视图描述
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (view_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE sys.tb_sys_view IS '视图表';
+COMMENT ON COLUMN sys.tb_sys_view.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_view.view_id IS '视图ID';
+COMMENT ON COLUMN sys.tb_sys_view.name IS '视图名称';
+COMMENT ON COLUMN sys.tb_sys_view.parent_id IS '父视图ID';
+COMMENT ON COLUMN sys.tb_sys_view.url IS '视图URL';
+COMMENT ON COLUMN sys.tb_sys_view.component IS '视图组件';
+COMMENT ON COLUMN sys.tb_sys_view.icon IS '视图图标';
+COMMENT ON COLUMN sys.tb_sys_view.type IS '视图类型';
+COMMENT ON COLUMN sys.tb_sys_view.layout IS '布局组件路径名称';
+COMMENT ON COLUMN sys.tb_sys_view.order_num IS '视图排序号';
+COMMENT ON COLUMN sys.tb_sys_view.description IS '视图描述';
+COMMENT ON COLUMN sys.tb_sys_view.dept_path IS '部门全路径';
+COMMENT ON COLUMN sys.tb_sys_view.creator IS '创建者';
+COMMENT ON COLUMN sys.tb_sys_view.updater IS '更新者';
+COMMENT ON COLUMN sys.tb_sys_view.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_view.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_view.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_view.deleted IS '是否删除';
+
+
+-- 模块表
+DROP TABLE IF EXISTS sys.tb_sys_module CASCADE;
+CREATE TABLE sys.tb_sys_module (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ module_id VARCHAR(50) NOT NULL, -- 模块ID
+ name VARCHAR(100) NOT NULL, -- 模块名称
+ description VARCHAR(255) DEFAULT NULL, -- 模块描述
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (module_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE sys.tb_sys_module IS '模块表';
+COMMENT ON COLUMN sys.tb_sys_module.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_module.module_id IS '模块ID';
+COMMENT ON COLUMN sys.tb_sys_module.name IS '模块名称';
+COMMENT ON COLUMN sys.tb_sys_module.description IS '模块描述';
+COMMENT ON COLUMN sys.tb_sys_module.creator IS '创建者';
+COMMENT ON COLUMN sys.tb_sys_module.dept_path IS '部门全路径';
+COMMENT ON COLUMN sys.tb_sys_module.updater IS '更新者';
+COMMENT ON COLUMN sys.tb_sys_module.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_module.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_module.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_module.deleted IS '是否删除';
+
+-- 权限表
+DROP TABLE IF EXISTS sys.tb_sys_permission CASCADE;
+CREATE TABLE sys.tb_sys_permission (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ permission_id VARCHAR(50) NOT NULL, -- 权限ID
+ name VARCHAR(100) NOT NULL, -- 权限名称
+ code VARCHAR(100) NOT NULL, -- 权限代码
+ description VARCHAR(255) DEFAULT NULL, -- 权限描述
+ module_id VARCHAR(50) DEFAULT NULL, -- 所属模块ID
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ dept_path VARCHAR(255) DEFAULT NULL,
+ status boolean NOT NULL DEFAULT false, -- 部门全路径
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (permission_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE sys.tb_sys_permission IS '权限表';
+COMMENT ON COLUMN sys.tb_sys_permission.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_permission.permission_id IS '权限ID';
+COMMENT ON COLUMN sys.tb_sys_permission.name IS '权限名称';
+COMMENT ON COLUMN sys.tb_sys_permission.code IS '权限代码';
+COMMENT ON COLUMN sys.tb_sys_permission.description IS '权限描述' ;
+COMMENT ON COLUMN sys.tb_sys_permission.module_id IS '所属模块ID';
+COMMENT ON COLUMN sys.tb_sys_permission.creator IS '创建者';
+COMMENT ON COLUMN sys.tb_sys_permission.dept_path IS '部门全路径';
+COMMENT ON COLUMN sys.tb_sys_permission.status IS '角色状态';
+COMMENT ON COLUMN sys.tb_sys_permission.updater IS '更新者';
+COMMENT ON COLUMN sys.tb_sys_permission.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_permission.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_permission.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_permission.deleted IS '是否删除';
+
+-- 角色权限
+DROP TABLE IF EXISTS sys.tb_sys_role_permission CASCADE;
+CREATE TABLE sys.tb_sys_role_permission (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ role_id VARCHAR(50) NOT NULL, -- 角色ID
+ permission_id VARCHAR(50) NOT NULL, -- 权限ID
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (role_id, permission_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE sys.tb_sys_role_permission IS '角色权限表';
+COMMENT ON COLUMN sys.tb_sys_role_permission.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_role_permission.role_id IS '角色ID';
+COMMENT ON COLUMN sys.tb_sys_role_permission.permission_id IS '权限ID';
+COMMENT ON COLUMN sys.tb_sys_role_permission.creator IS '创建者';
+COMMENT ON COLUMN sys.tb_sys_role_permission.dept_path IS '部门全路径';
+COMMENT ON COLUMN sys.tb_sys_role_permission.updater IS '更新者';
+COMMENT ON COLUMN sys.tb_sys_role_permission.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_role_permission.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_role_permission.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_role_permission.deleted IS '是否删除';
+
+-- 视图权限
+DROP TABLE IF EXISTS sys.tb_sys_view_permission CASCADE;
+CREATE TABLE sys.tb_sys_view_permission (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ view_id VARCHAR(50) NOT NULL, -- 视图ID
+ permission_id VARCHAR(50) NOT NULL, -- 权限ID
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (view_id, permission_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE sys.tb_sys_view_permission IS '视图权限表';
+COMMENT ON COLUMN sys.tb_sys_view_permission.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_view_permission.view_id IS '视图ID';
+COMMENT ON COLUMN sys.tb_sys_view_permission.permission_id IS '权限ID';
+COMMENT ON COLUMN sys.tb_sys_view_permission.creator IS '创建者';
+COMMENT ON COLUMN sys.tb_sys_view_permission.dept_path IS '部门全路径';
+COMMENT ON COLUMN sys.tb_sys_view_permission.updater IS '更新者';
+COMMENT ON COLUMN sys.tb_sys_view_permission.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_view_permission.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_view_permission.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_view_permission.deleted IS '是否删除';
+
+-- 为所有表创建更新时间触发器
+-- DROP TRIGGER IF EXISTS trg_tb_sys_dept_update_time ON sys.tb_sys_dept;
+-- CREATE TRIGGER trg_tb_sys_dept_update_time
+-- BEFORE UPDATE ON sys.tb_sys_dept
+-- FOR EACH ROW
+-- EXECUTE FUNCTION sys.set_update_time();
+
+-- DROP TRIGGER IF EXISTS trg_tb_sys_role_update_time ON sys.tb_sys_role;
+-- CREATE TRIGGER trg_tb_sys_role_update_time
+-- BEFORE UPDATE ON sys.tb_sys_role
+-- FOR EACH ROW
+-- EXECUTE FUNCTION sys.set_update_time();
+
+-- DROP TRIGGER IF EXISTS trg_tb_sys_dept_role_update_time ON sys.tb_sys_dept_role;
+-- CREATE TRIGGER trg_tb_sys_dept_role_update_time
+-- BEFORE UPDATE ON sys.tb_sys_dept_role
+-- FOR EACH ROW
+-- EXECUTE FUNCTION sys.set_update_time();
+
+-- DROP TRIGGER IF EXISTS trg_tb_sys_user_role_update_time ON sys.tb_sys_user_role;
+-- CREATE TRIGGER trg_tb_sys_user_role_update_time
+-- BEFORE UPDATE ON sys.tb_sys_user_role
+-- FOR EACH ROW
+-- EXECUTE FUNCTION sys.set_update_time();
+
+-- DROP TRIGGER IF EXISTS trg_tb_sys_view_update_time ON sys.tb_sys_view;
+-- CREATE TRIGGER trg_tb_sys_view_update_time
+-- BEFORE UPDATE ON sys.tb_sys_view
+-- FOR EACH ROW
+-- EXECUTE FUNCTION sys.set_update_time();
+
+-- DROP TRIGGER IF EXISTS trg_tb_sys_module_update_time ON sys.tb_sys_module;
+-- CREATE TRIGGER trg_tb_sys_module_update_time
+-- BEFORE UPDATE ON sys.tb_sys_module
+-- FOR EACH ROW
+-- EXECUTE FUNCTION sys.set_update_time();
+
+-- DROP TRIGGER IF EXISTS trg_tb_sys_permission_update_time ON sys.tb_sys_permission;
+-- CREATE TRIGGER trg_tb_sys_permission_update_time
+-- BEFORE UPDATE ON sys.tb_sys_permission
+-- FOR EACH ROW
+-- EXECUTE FUNCTION sys.set_update_time();
+
+-- DROP TRIGGER IF EXISTS trg_tb_sys_role_permission_update_time ON sys.tb_sys_role_permission;
+-- CREATE TRIGGER trg_tb_sys_role_permission_update_time
+-- BEFORE UPDATE ON sys.tb_sys_role_permission
+-- FOR EACH ROW
+-- EXECUTE FUNCTION sys.set_update_time();
+
+-- DROP TRIGGER IF EXISTS trg_tb_sys_view_permission_update_time ON sys.tb_sys_view_permission;
+-- CREATE TRIGGER trg_tb_sys_view_permission_update_time
+-- BEFORE UPDATE ON sys.tb_sys_view_permission
+-- FOR EACH ROW
+-- EXECUTE FUNCTION sys.set_update_time();
+
+-- =============================
+-- 通用对象级权限(ACL)
+-- 支持对象类型:任意(如 article/file/course/...)
+-- 支持主体:user/dept/role
+-- 权限位:1=读(read),2=写(write),4=执行(exec),可相加
+-- include_descendants:当主体为 dept/role 时,是否包含其子级(便于“所有子级可查看”场景)
+-- allow:true=允许;false=显式拒绝(拒绝优先生效)
+-- dept_path:用于数据范围隔离(与现有规则一致)
+-- =============================
+
+DROP TABLE IF EXISTS sys.tb_sys_acl CASCADE;
+CREATE TABLE sys.tb_sys_acl (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ acl_id VARCHAR(50) NOT NULL, -- ACL主键
+ object_type VARCHAR(50) NOT NULL, -- 对象类型:article/file/course/...
+ object_id VARCHAR(50) NOT NULL, -- 对象ID
+ principal_type VARCHAR(20) NOT NULL, -- 主体类型:user/dept/role
+ principal_id VARCHAR(50) NOT NULL, -- 主体ID
+ principal_dept_id VARCHAR(50) DEFAULT NULL, -- 当主体为role且限定到某部门时的部门ID(支持“某部门的某角色”)
+ permission SMALLINT NOT NULL, -- 权限位:1读 2写 4执行
+ allow BOOLEAN NOT NULL DEFAULT true, -- 允许或显式拒绝
+ include_descendants BOOLEAN NOT NULL DEFAULT false, -- 是否包含子级(对dept/role生效)
+ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离)
+ creator VARCHAR(50) DEFAULT NULL, -- 创建者
+ updater VARCHAR(50) DEFAULT NULL, -- 更新者
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (acl_id),
+ UNIQUE (object_type, object_id, principal_type, principal_id, principal_dept_id, deleted),
+ UNIQUE (optsn)
+);
+
+COMMENT ON TABLE sys.tb_sys_acl IS '通用对象级权限表(ACL)';
+COMMENT ON COLUMN sys.tb_sys_acl.object_type IS '对象类型:article/file/course/...';
+COMMENT ON COLUMN sys.tb_sys_acl.object_id IS '对象ID';
+COMMENT ON COLUMN sys.tb_sys_acl.principal_type IS '主体类型:user/dept/role';
+COMMENT ON COLUMN sys.tb_sys_acl.principal_id IS '主体ID';
+COMMENT ON COLUMN sys.tb_sys_acl.principal_dept_id IS '当主体为角色时,可选的限定部门ID(某部门的某角色)';
+COMMENT ON COLUMN sys.tb_sys_acl.permission IS '权限位:1读 2写 4执行,可相加';
+COMMENT ON COLUMN sys.tb_sys_acl.allow IS '是否允许(false=显式拒绝)';
+COMMENT ON COLUMN sys.tb_sys_acl.include_descendants IS '是否包含子级(dept/role时启用)';
+COMMENT ON COLUMN sys.tb_sys_acl.dept_path IS '部门全路径(用于数据范围隔离)';
+
+CREATE INDEX idx_sys_acl_object ON sys.tb_sys_acl USING btree (object_type, object_id) WHERE deleted = false;
+CREATE INDEX idx_sys_acl_principal ON sys.tb_sys_acl USING btree (principal_type, principal_id) WHERE deleted = false;
+CREATE INDEX idx_sys_acl_dept_path ON sys.tb_sys_acl USING btree (dept_path) WHERE deleted = false;
+-- 针对“某部门的某角色”的检索优化
+CREATE INDEX idx_sys_acl_role_scoped ON sys.tb_sys_acl USING btree (principal_id, principal_dept_id)
+ WHERE deleted = false AND principal_type = 'role';
+
+-- =============================
+-- ACL 策略表:定义对象类型的层级可见/可编辑规则
+-- 例如:
+-- - 同级与父级管理员可修改(edit_hierarchy_rule = 'parent_or_same_admin')
+-- - 子级全部可查看(view_hierarchy_rule = 'children_all')
+-- - 课程:下级只有指定部门/角色可查看(view_hierarchy_rule = 'children_specified')
+-- 说明:业务侧需结合“管理员”的判定(通常由角色判定)在应用层实现;本表做规则声明
+-- =============================
+
+DROP TABLE IF EXISTS sys.tb_sys_acl_policy CASCADE;
+CREATE TABLE sys.tb_sys_acl_policy (
+ optsn VARCHAR(50) NOT NULL, -- 流水号
+ policy_id VARCHAR(50) NOT NULL, -- 策略ID
+ name VARCHAR(255) NOT NULL, -- 策略名称
+ object_type VARCHAR(50) NOT NULL, -- 目标对象类型
+ edit_hierarchy_rule VARCHAR(50) DEFAULT NULL, -- 编辑层级规则:parent_only/parent_or_same_admin/owner_only/none
+ view_hierarchy_rule VARCHAR(50) DEFAULT NULL, -- 查看层级规则:children_all/children_specified/none
+ default_permission SMALLINT DEFAULT 0, -- 默认权限位(无显式ACL时应用)
+ default_allow BOOLEAN DEFAULT true, -- 默认是否允许
+ apply_to_children BOOLEAN DEFAULT true, -- 是否默认应用到子级
+ creator VARCHAR(50) DEFAULT NULL,
+ updater VARCHAR(50) DEFAULT NULL,
+ create_time timestamptz NOT NULL DEFAULT now(),
+ update_time timestamptz DEFAULT NULL,
+ delete_time timestamptz DEFAULT NULL,
+ deleted boolean NOT NULL DEFAULT false,
+ PRIMARY KEY (policy_id),
+ UNIQUE (object_type, deleted),
+ UNIQUE (optsn)
+);
+
+COMMENT ON TABLE sys.tb_sys_acl_policy IS 'ACL对象类型策略表(层级可见/可编辑规则)';
+COMMENT ON COLUMN sys.tb_sys_acl_policy.object_type IS '对象类型:article/file/course/...';
+COMMENT ON COLUMN sys.tb_sys_acl_policy.edit_hierarchy_rule IS '编辑层级规则:parent_only/parent_or_same_admin/owner_only/none';
+COMMENT ON COLUMN sys.tb_sys_acl_policy.view_hierarchy_rule IS '查看层级规则:children_all/children_specified/none';
+COMMENT ON COLUMN sys.tb_sys_acl_policy.default_permission IS '默认权限位(无显式ACL时兜底)';
+COMMENT ON COLUMN sys.tb_sys_acl_policy.apply_to_children IS '默认是否应用到子级';
+
+CREATE INDEX idx_sys_acl_policy_object ON sys.tb_sys_acl_policy USING btree (object_type) WHERE deleted = false;
\ No newline at end of file
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableUser.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableUser.sql
new file mode 100644
index 0000000..eab2fc8
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableUser.sql
@@ -0,0 +1,120 @@
+
+-- 创建 sys schema(如果不存在)
+CREATE SCHEMA IF NOT EXISTS sys;
+
+-- 用户表
+DROP TABLE IF EXISTS sys.tb_sys_user CASCADE;
+CREATE TABLE sys.tb_sys_user (
+ optsn varchar(50) NOT NULL, -- 流水号
+ user_id varchar(50) NOT NULL, -- 用户ID
+ password varchar(128) NOT NULL, -- 密码(建议存储 bcrypt/argon2 哈希)
+ email varchar(100), -- 电子邮件
+ phone varchar(20), -- 电话号码
+ wechat_id varchar(50), -- 微信ID
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间(使用带时区时间)
+ update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除(使用 boolean)
+ status integer NOT NULL DEFAULT 1, -- 状态
+ PRIMARY KEY (user_id),
+ UNIQUE (optsn),
+ UNIQUE (email),
+ UNIQUE (phone),
+ UNIQUE (wechat_id)
+);
+CREATE INDEX idx_tb_sys_user_phone ON sys.tb_sys_user USING btree (phone);
+
+-- 按 email 域名建立表达式索引(对域名小写处理以实现不区分大小写的域名查询)
+-- 使用 split_part(email, '@', 2) 提取 @ 之后的域名部分,再做 lower() 归一化
+-- WHERE email IS NOT NULL 可以避免索引包含大量 NULL
+CREATE INDEX idx_tb_sys_user_email_domain ON sys.tb_sys_user USING btree (lower(split_part(email, '@', 2)))
+ WHERE email IS NOT NULL;
+CREATE INDEX idx_tb_sys_user_wechat_id ON sys.tb_sys_user USING btree (wechat_id);
+
+-- 可选:保留列注释(如果你想把 MySQL 的 COMMENT 同步到 Postgres)
+COMMENT ON TABLE sys.tb_sys_user IS '用户表';
+COMMENT ON COLUMN sys.tb_sys_user.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_user.user_id IS '用户ID';
+COMMENT ON COLUMN sys.tb_sys_user.password IS '密码(建议存储 bcrypt/argon2 哈希)';
+COMMENT ON COLUMN sys.tb_sys_user.email IS '电子邮件';
+COMMENT ON COLUMN sys.tb_sys_user.phone IS '电话号码';
+COMMENT ON COLUMN sys.tb_sys_user.wechat_id IS '微信ID';
+COMMENT ON COLUMN sys.tb_sys_user.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_user.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_user.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_user.deleted IS '是否删除';
+COMMENT ON COLUMN sys.tb_sys_user.status IS '状态';
+
+-- 用户信息表
+DROP TABLE IF EXISTS sys.tb_sys_user_info CASCADE;
+CREATE TABLE sys.tb_sys_user_info (
+ optsn varchar(50) NOT NULL, -- 流水号
+ user_id varchar(50) NOT NULL, -- 用户ID
+ avatar varchar(255), -- 头像
+ gender integer DEFAULT 0, -- 性别
+ family_name varchar(50), -- 姓
+ given_name varchar(50), -- 名
+ full_name varchar(100), -- 全名
+ level integer DEFAULT 1, -- 等级
+ id_card varchar(50), -- 身份证号
+ address varchar(255), -- 地址
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ update_time timestamptz DEFAULT NULL, -- 更新时间(触发器维护)
+ delete_time timestamptz DEFAULT NULL, -- 删除时间
+ deleted boolean NOT NULL DEFAULT false, -- 是否删除
+ PRIMARY KEY (user_id),
+ UNIQUE (optsn)
+);
+COMMENT ON TABLE sys.tb_sys_user_info IS '用户信息表';
+COMMENT ON COLUMN sys.tb_sys_user_info.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_user_info.user_id IS '用户ID';
+COMMENT ON COLUMN sys.tb_sys_user_info.avatar IS '头像';
+COMMENT ON COLUMN sys.tb_sys_user_info.gender IS '性别';
+COMMENT ON COLUMN sys.tb_sys_user_info.family_name IS '姓';
+COMMENT ON COLUMN sys.tb_sys_user_info.given_name IS '名';
+COMMENT ON COLUMN sys.tb_sys_user_info.full_name IS '全名';
+COMMENT ON COLUMN sys.tb_sys_user_info.level IS '等级';
+COMMENT ON COLUMN sys.tb_sys_user_info.id_card IS '身份证号';
+COMMENT ON COLUMN sys.tb_sys_user_info.address IS '地址';
+COMMENT ON COLUMN sys.tb_sys_user_info.create_time IS '创建时间';
+COMMENT ON COLUMN sys.tb_sys_user_info.update_time IS '更新时间';
+COMMENT ON COLUMN sys.tb_sys_user_info.delete_time IS '删除时间';
+COMMENT ON COLUMN sys.tb_sys_user_info.deleted IS '是否删除';
+
+-- 登录日志表
+DROP TABLE IF EXISTS sys.tb_sys_login_log CASCADE;
+CREATE TABLE sys.tb_sys_login_log (
+ optsn varchar(50) NOT NULL, -- 流水号(作为主键)
+ user_id varchar(50) NOT NULL, -- 用户ID
+ username varchar(50) NOT NULL, -- 用户名
+ ip_address varchar(45), -- IP地址
+ ip_source varchar(100), -- IP来源
+ browser varchar(100), -- 浏览器
+ os varchar(100), -- 操作系统
+ password varchar(128), -- 密码(建议存储 bcrypt/argon2 哈希)
+ login_time timestamptz DEFAULT now(), -- 登录时间
+ status integer DEFAULT 1, -- 登录状态(0失败 1成功)
+ error_count integer DEFAULT 0, -- 错误次数
+ message varchar(255), -- 登录消息
+ create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间
+ PRIMARY KEY (optsn)
+);
+-- B-tree 索引(显式指定 USING btree,Postgres 默认即为 btree)
+CREATE INDEX idx_tb_sys_login_log_user_id ON sys.tb_sys_login_log USING btree (user_id);
+CREATE INDEX idx_tb_sys_login_log_login_time ON sys.tb_sys_login_log USING btree (login_time);
+
+-- 可选:保留列注释
+COMMENT ON TABLE sys.tb_sys_login_log IS '登录日志表';
+COMMENT ON COLUMN sys.tb_sys_login_log.optsn IS '流水号';
+COMMENT ON COLUMN sys.tb_sys_login_log.user_id IS '用户ID';
+COMMENT ON COLUMN sys.tb_sys_login_log.username IS '用户名';
+COMMENT ON COLUMN sys.tb_sys_login_log.ip_address IS 'IP地址';
+COMMENT ON COLUMN sys.tb_sys_login_log.ip_source IS 'IP来源';
+COMMENT ON COLUMN sys.tb_sys_login_log.browser IS '浏览器';
+COMMENT ON COLUMN sys.tb_sys_login_log.os IS '操作系统';
+COMMENT ON COLUMN sys.tb_sys_login_log.password IS '密码(建议存储 bcrypt/argon2 哈希)';
+COMMENT ON COLUMN sys.tb_sys_login_log.login_time IS '登录时间';
+COMMENT ON COLUMN sys.tb_sys_login_log.status IS '登录状态(0失败 1成功)';
+COMMENT ON COLUMN sys.tb_sys_login_log.error_count IS '错误次数';
+COMMENT ON COLUMN sys.tb_sys_login_log.message IS '登录消息';
+COMMENT ON COLUMN sys.tb_sys_login_log.create_time IS '创建时间';
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/initAll.sql b/urbanLifelineServ/.bin/database/postgres/sql/initAll.sql
new file mode 100644
index 0000000..83873ab
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/initAll.sql
@@ -0,0 +1,11 @@
+-- 按顺序执行初始化
+\i createDB.sql -- 创建数据库和基础扩展
+\i createTableUser.sql -- 用户表相关(核心表)
+\i createTablePermission.sql -- 权限相关(核心表)
+\i createTableFile.sql -- 文件管理
+\i createTableMessage.sql -- 消息管理
+\i createTableConfig.sql -- 配置管理
+\i createTableLog.sql -- 日志管理
+
+-- 初始化基础数据(如果有)
+-- \i initBaseData.sql -- 取消注释如果需要初始化基础数据
\ No newline at end of file
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/initDataConfig.sql b/urbanLifelineServ/.bin/database/postgres/sql/initDataConfig.sql
new file mode 100644
index 0000000..28eecd0
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/initDataConfig.sql
@@ -0,0 +1,43 @@
+-- 初始化系统常用配置(与 config.tb_sys_config 对应)
+-- 仅插入常用示例,可按需调整 value/remark
+
+INSERT INTO config.tb_sys_config (
+ optsn, config_id, key, value, type, description, "group", "order", status, remark,
+ creator, dept_path, updater, create_time, update_time, delete_time, deleted
+) VALUES
+
+-- 站点与品牌
+('CFG-0001', 'cfg_site_name', 'site.name', 'urban-lifeline 平台', 'string', '站点名称', 'site', 10, 'enabled', '展示在标题/登录/页脚', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0002', 'cfg_site_logo', 'site.logo', '/static/logo.png', 'string', '站点Logo地址', 'site', 20, 'enabled', '相对或绝对URL', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0003', 'cfg_site_icp', 'site.icp', '', 'string', 'ICP备案号', 'site', 30, 'enabled', '页脚展示', 'system', NULL, NULL, now(), NULL, NULL, false),
+
+-- 国际化与时区
+('CFG-0101', 'cfg_i18n_locale', 'i18n.defaultLocale', 'zh-CN', 'string', '默认语言', 'i18n', 10, 'enabled', '如 zh-CN/en-US', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0102', 'cfg_timezone', 'system.timezone', 'Asia/Shanghai', 'string', '系统默认时区', 'i18n', 20, 'enabled', 'IANA时区名', 'system', NULL, NULL, now(), NULL, NULL, false),
+
+-- 安全与认证
+('CFG-0201', 'cfg_pwd_policy', 'security.passwordPolicy','{"minLen":8,"upper":1,"lower":1,"digit":1,"special":0}', 'json', '密码策略', 'security', 10, 'enabled', 'JSON结构', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0202', 'cfg_jwt_exp', 'security.jwt.expireSeconds','86400', 'number', 'JWT过期秒数', 'security', 20, 'enabled', '默认24小时', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0203', 'cfg_session_timeout', 'security.session.timeoutMinutes','30', 'number', '会话超时(分钟)', 'security', 30, 'enabled', '空闲登出', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0204', 'cfg_signup_enabled', 'security.signup.enabled','false', 'bool', '是否开放注册', 'security', 40, 'enabled', '生产建议关闭', 'system', NULL, NULL, now(), NULL, NULL, false),
+
+-- 存储与上传
+('CFG-0301', 'cfg_upload_max', 'upload.maxSizeMB', '50', 'number', '单文件最大上传(MB)', 'storage', 10, 'enabled', '前后端需一致校验', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0302', 'cfg_storage_backend', 'storage.backend', 'local', 'string', '存储后端(local/minio/s3)', 'storage', 20, 'enabled', '本地/MinIO/S3等', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0303', 'cfg_storage_base', 'storage.basePath', '/data/urban-lifeline', 'string', '本地存储基路径', 'storage', 30, 'enabled', '当 backend=local', 'system', NULL, NULL, now(), NULL, NULL, false),
+
+-- 通知(邮件/SMS)
+('CFG-0401', 'cfg_mail_host', 'mail.smtp.host', '', 'string', 'SMTP主机', 'notify', 10, 'disabled','留空为未配置', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0402', 'cfg_mail_port', 'mail.smtp.port', '465', 'number', 'SMTP端口', 'notify', 20, 'disabled','SSL常用465', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0403', 'cfg_mail_from', 'mail.from', '', 'string', '发件人邮箱', 'notify', 30, 'disabled','如 no-reply@x.com', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0411', 'cfg_sms_provider', 'sms.provider', '', 'string', '短信服务商', 'notify', 40, 'disabled','如 aliyun/tencent', 'system', NULL, NULL, now(), NULL, NULL, false),
+
+-- 日志与审计
+('CFG-0501', 'cfg_log_level', 'log.level', 'INFO', 'string', '系统日志级别', 'log', 10, 'enabled', 'DEBUG/INFO/WARN/ERROR', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0502', 'cfg_audit_retention', 'audit.retentionDays', '90', 'number', '审计日志保留天数', 'log', 20, 'enabled', '合规按需调整', 'system', NULL, NULL, now(), NULL, NULL, false),
+
+-- 平台特性
+('CFG-0601', 'cfg_maintenance', 'platform.maintenance', 'false', 'bool', '维护模式开关', 'platform', 10, 'enabled', 'true时仅管理员可用', 'system', NULL, NULL, now(), NULL, NULL, false),
+('CFG-0602', 'cfg_feature_acl_policy', 'feature.acl.policy', 'enabled', 'string', 'ACL策略开关', 'platform', 20, 'enabled', 'enabled/disabled', 'system', NULL, NULL, now(), NULL, NULL, false);
+
+
diff --git a/urbanLifelineServ/.bin/database/postgres/sql/optimizations.sql b/urbanLifelineServ/.bin/database/postgres/sql/optimizations.sql
new file mode 100644
index 0000000..64af1ec
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/postgres/sql/optimizations.sql
@@ -0,0 +1,549 @@
+-- =============================
+-- 数据库优化补丁脚本
+-- 基于现有表结构的增强和修改
+-- =============================
+
+-- =============================
+-- 1. 用户模块优化
+-- =============================
+
+-- 1.1 移除登录日志表中的敏感密码字段
+DO $$
+BEGIN
+ IF EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = 'sys'
+ AND table_name = 'tb_sys_login_log'
+ AND column_name = 'password'
+ ) THEN
+ ALTER TABLE sys.tb_sys_login_log DROP COLUMN password;
+ RAISE NOTICE '已移除登录日志表的密码字段';
+ END IF;
+END $$;
+
+-- 1.2 创建用户部门关联表
+CREATE TABLE IF NOT EXISTS sys.tb_sys_user_dept (
+ optsn VARCHAR(50) NOT NULL,
+ user_id VARCHAR(50) NOT NULL,
+ dept_id VARCHAR(50) NOT NULL,
+ is_primary BOOLEAN DEFAULT false,
+ position VARCHAR(100),
+ creator VARCHAR(50) DEFAULT NULL,
+ updater VARCHAR(50) DEFAULT NULL,
+ create_time timestamptz NOT NULL DEFAULT now(),
+ update_time timestamptz DEFAULT NULL,
+ delete_time timestamptz DEFAULT NULL,
+ deleted BOOLEAN NOT NULL DEFAULT false,
+ PRIMARY KEY (user_id, dept_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (user_id) REFERENCES sys.tb_sys_user(user_id),
+ FOREIGN KEY (dept_id) REFERENCES sys.tb_sys_dept(dept_id)
+);
+
+COMMENT ON TABLE sys.tb_sys_user_dept IS '用户部门关联表';
+COMMENT ON COLUMN sys.tb_sys_user_dept.is_primary IS '是否主部门';
+COMMENT ON COLUMN sys.tb_sys_user_dept.position IS '职位';
+
+CREATE INDEX IF NOT EXISTS idx_user_dept_user ON sys.tb_sys_user_dept(user_id) WHERE deleted = false;
+CREATE INDEX IF NOT EXISTS idx_user_dept_dept ON sys.tb_sys_user_dept(dept_id) WHERE deleted = false;
+
+-- 1.3 用户表添加主部门字段
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = 'sys'
+ AND table_name = 'tb_sys_user'
+ AND column_name = 'primary_dept_id'
+ ) THEN
+ ALTER TABLE sys.tb_sys_user ADD COLUMN primary_dept_id VARCHAR(50);
+ COMMENT ON COLUMN sys.tb_sys_user.primary_dept_id IS '主部门ID';
+ END IF;
+END $$;
+
+-- =============================
+-- 2. 权限模块优化
+-- =============================
+
+-- 2.1 角色表添加排序字段
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = 'sys'
+ AND table_name = 'tb_sys_role'
+ AND column_name = 'order_num'
+ ) THEN
+ ALTER TABLE sys.tb_sys_role ADD COLUMN order_num INTEGER DEFAULT 0;
+ COMMENT ON COLUMN sys.tb_sys_role.order_num IS '排序号';
+ END IF;
+END $$;
+
+-- 2.2 权限表添加权限类型字段
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = 'sys'
+ AND table_name = 'tb_sys_permission'
+ AND column_name = 'permission_type'
+ ) THEN
+ ALTER TABLE sys.tb_sys_permission ADD COLUMN permission_type VARCHAR(20) DEFAULT 'action';
+ COMMENT ON COLUMN sys.tb_sys_permission.permission_type IS '权限类型:action-操作权限/data-数据权限/menu-菜单权限';
+ END IF;
+END $$;
+
+-- =============================
+-- 3. 文件模块扩展
+-- =============================
+
+-- 3.1 文件表添加版本管理字段
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = 'file'
+ AND table_name = 'tb_sys_file'
+ AND column_name = 'version'
+ ) THEN
+ ALTER TABLE file.tb_sys_file
+ ADD COLUMN version VARCHAR(20) DEFAULT '1.0',
+ ADD COLUMN parent_file_id VARCHAR(50),
+ ADD COLUMN is_latest BOOLEAN DEFAULT true;
+
+ COMMENT ON COLUMN file.tb_sys_file.version IS '文件版本号';
+ COMMENT ON COLUMN file.tb_sys_file.parent_file_id IS '父文件ID(版本链)';
+ COMMENT ON COLUMN file.tb_sys_file.is_latest IS '是否最新版本';
+ END IF;
+END $$;
+
+-- 3.2 文件表添加分类和标签
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = 'file'
+ AND table_name = 'tb_sys_file'
+ AND column_name = 'category'
+ ) THEN
+ ALTER TABLE file.tb_sys_file
+ ADD COLUMN category VARCHAR(100),
+ ADD COLUMN tags TEXT[],
+ ADD COLUMN metadata JSONB;
+
+ COMMENT ON COLUMN file.tb_sys_file.category IS '文件分类';
+ COMMENT ON COLUMN file.tb_sys_file.tags IS '文件标签数组';
+ COMMENT ON COLUMN file.tb_sys_file.metadata IS '文件元数据';
+ END IF;
+END $$;
+
+-- 3.3 创建文件关联表
+CREATE TABLE IF NOT EXISTS file.tb_file_relation (
+ optsn VARCHAR(50) NOT NULL,
+ relation_id VARCHAR(50) NOT NULL,
+ file_id VARCHAR(50) NOT NULL,
+ object_type VARCHAR(50) NOT NULL,
+ object_id VARCHAR(50) NOT NULL,
+ relation_type VARCHAR(30) DEFAULT 'attachment',
+ order_num INTEGER DEFAULT 0,
+ creator VARCHAR(50) DEFAULT NULL,
+ updater VARCHAR(50) DEFAULT NULL,
+ create_time timestamptz NOT NULL DEFAULT now(),
+ update_time timestamptz DEFAULT NULL,
+ delete_time timestamptz DEFAULT NULL,
+ deleted BOOLEAN NOT NULL DEFAULT false,
+ PRIMARY KEY (relation_id),
+ UNIQUE (optsn),
+ FOREIGN KEY (file_id) REFERENCES file.tb_sys_file(file_id)
+);
+
+COMMENT ON TABLE file.tb_file_relation IS '文件关联表';
+COMMENT ON COLUMN file.tb_file_relation.object_type IS '对象类型:bidding_project/ticket/document等';
+COMMENT ON COLUMN file.tb_file_relation.object_id IS '对象ID';
+COMMENT ON COLUMN file.tb_file_relation.relation_type IS '关联类型:attachment-附件/avatar-头像/banner-横幅';
+
+CREATE INDEX IF NOT EXISTS idx_file_relation_object
+ON file.tb_file_relation(object_type, object_id) WHERE deleted = false;
+CREATE INDEX IF NOT EXISTS idx_file_relation_file
+ON file.tb_file_relation(file_id) WHERE deleted = false;
+
+-- =============================
+-- 4. 消息模块增强
+-- =============================
+
+-- 4.1 创建消息模板表
+CREATE TABLE IF NOT EXISTS message.tb_message_template (
+ optsn VARCHAR(50) NOT NULL,
+ template_id VARCHAR(50) NOT NULL,
+ template_code VARCHAR(100) NOT NULL,
+ template_name VARCHAR(255) NOT NULL,
+ template_type VARCHAR(30) NOT NULL,
+ title_template TEXT,
+ content_template TEXT NOT NULL,
+ variables JSONB,
+ dept_path VARCHAR(255) DEFAULT NULL,
+ creator VARCHAR(50) DEFAULT NULL,
+ updater VARCHAR(50) DEFAULT NULL,
+ create_time timestamptz NOT NULL DEFAULT now(),
+ update_time timestamptz DEFAULT NULL,
+ delete_time timestamptz DEFAULT NULL,
+ deleted BOOLEAN NOT NULL DEFAULT false,
+ PRIMARY KEY (template_id),
+ UNIQUE (optsn),
+ UNIQUE (template_code)
+);
+
+COMMENT ON TABLE message.tb_message_template IS '消息模板表';
+COMMENT ON COLUMN message.tb_message_template.template_type IS '模板类型:system-系统/business-业务';
+COMMENT ON COLUMN message.tb_message_template.variables IS '模板变量定义';
+
+CREATE INDEX IF NOT EXISTS idx_template_type
+ON message.tb_message_template(template_type) WHERE deleted = false;
+
+-- =============================
+-- 5. 日志模块优化
+-- =============================
+
+-- 5.1 日志表添加trace_id用于链路追踪
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = 'log'
+ AND table_name = 'tb_sys_log'
+ AND column_name = 'trace_id'
+ ) THEN
+ ALTER TABLE log.tb_sys_log
+ ADD COLUMN trace_id VARCHAR(50),
+ ADD COLUMN span_id VARCHAR(50),
+ ADD COLUMN parent_span_id VARCHAR(50);
+
+ COMMENT ON COLUMN log.tb_sys_log.trace_id IS '追踪ID(用于分布式追踪)';
+ COMMENT ON COLUMN log.tb_sys_log.span_id IS '跨度ID';
+ COMMENT ON COLUMN log.tb_sys_log.parent_span_id IS '父跨度ID';
+
+ CREATE INDEX idx_log_trace ON log.tb_sys_log(trace_id) WHERE trace_id IS NOT NULL;
+ END IF;
+END $$;
+
+-- =============================
+-- 6. 全文搜索索引
+-- =============================
+
+-- 6.1 为知识文档标题创建全文搜索索引
+CREATE INDEX IF NOT EXISTS idx_knowledge_doc_title_trgm
+ON knowledge.tb_knowledge_document USING gin(title gin_trgm_ops)
+WHERE deleted = false;
+
+-- 6.2 为招标项目名称创建全文搜索索引
+CREATE INDEX IF NOT EXISTS idx_project_name_trgm
+ON bidding.tb_bidding_project USING gin(project_name gin_trgm_ops)
+WHERE deleted = false;
+
+-- 6.3 为客户姓名创建全文搜索索引
+CREATE INDEX IF NOT EXISTS idx_customer_name_trgm
+ON customer_service.tb_customer USING gin(customer_name gin_trgm_ops)
+WHERE deleted = false;
+
+-- =============================
+-- 7. JSONB字段优化索引
+-- =============================
+
+-- 7.1 智能体配置JSONB索引
+CREATE INDEX IF NOT EXISTS idx_agent_model_config_gin
+ON agent.tb_agent USING gin(model_config)
+WHERE deleted = false;
+
+-- 7.2 工单元数据JSONB索引
+CREATE INDEX IF NOT EXISTS idx_ticket_metadata_gin
+ON customer_service.tb_ticket USING gin(metadata)
+WHERE deleted = false;
+
+-- =============================
+-- 8. 性能优化视图
+-- =============================
+
+-- 8.1 用户完整权限视图(包含ACL)
+CREATE OR REPLACE VIEW sys.v_user_full_permissions AS
+WITH user_roles AS (
+ -- 用户直接拥有的角色权限
+ SELECT DISTINCT
+ ur.user_id,
+ p.permission_id,
+ p.code AS permission_code,
+ p.name AS permission_name,
+ 'role' AS source_type,
+ r.role_id AS source_id
+ FROM sys.tb_sys_user_role ur
+ JOIN sys.tb_sys_role r ON ur.role_id = r.role_id
+ JOIN sys.tb_sys_role_permission rp ON r.role_id = rp.role_id
+ JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
+ WHERE ur.deleted = false
+ AND r.deleted = false
+ AND rp.deleted = false
+ AND p.deleted = false
+),
+user_acls AS (
+ -- 用户的ACL权限
+ SELECT DISTINCT
+ principal_id AS user_id,
+ object_type || ':' || object_id AS permission_id,
+ object_type || '_' ||
+ CASE
+ WHEN (permission & 1) = 1 THEN 'read'
+ WHEN (permission & 2) = 2 THEN 'write'
+ WHEN (permission & 4) = 4 THEN 'exec'
+ END AS permission_code,
+ 'ACL permission on ' || object_type AS permission_name,
+ 'acl' AS source_type,
+ acl_id AS source_id
+ FROM sys.tb_sys_acl
+ WHERE principal_type = 'user'
+ AND allow = true
+ AND deleted = false
+)
+SELECT * FROM user_roles
+UNION ALL
+SELECT * FROM user_acls;
+
+COMMENT ON VIEW sys.v_user_full_permissions IS '用户完整权限视图(包含角色权限和ACL权限)';
+
+-- 8.2 智能体实时状态视图
+CREATE OR REPLACE VIEW agent.v_agent_realtime_status AS
+SELECT
+ a.agent_id,
+ a.agent_name,
+ a.agent_type,
+ a.status,
+ COUNT(DISTINCT s.session_id) FILTER (WHERE s.session_status = 'active') AS active_sessions,
+ COUNT(DISTINCT s.user_id) FILTER (WHERE s.start_time > now() - interval '24 hours') AS daily_users,
+ COALESCE(SUM(s.message_count) FILTER (WHERE s.start_time > now() - interval '1 hour'), 0) AS hourly_messages,
+ COALESCE(AVG(r.rating) FILTER (WHERE r.create_time > now() - interval '7 days'), 0) AS weekly_avg_rating
+FROM agent.tb_agent a
+LEFT JOIN agent.tb_agent_session s ON a.agent_id = s.agent_id AND s.deleted = false
+LEFT JOIN agent.tb_agent_rating r ON a.agent_id = r.agent_id AND r.deleted = false
+WHERE a.deleted = false
+GROUP BY a.agent_id, a.agent_name, a.agent_type, a.status;
+
+COMMENT ON VIEW agent.v_agent_realtime_status IS '智能体实时状态视图';
+
+-- 8.3 工单处理效率视图
+CREATE OR REPLACE VIEW customer_service.v_ticket_efficiency AS
+SELECT
+ DATE(t.create_time) AS stat_date,
+ t.ticket_type,
+ t.priority,
+ COUNT(*) AS total_tickets,
+ COUNT(*) FILTER (WHERE t.ticket_status = 'resolved') AS resolved_tickets,
+ COUNT(*) FILTER (WHERE t.is_overdue) AS overdue_tickets,
+ AVG(EXTRACT(EPOCH FROM (t.response_time - t.create_time))/60) AS avg_response_minutes,
+ AVG(EXTRACT(EPOCH FROM (t.resolution_time - t.create_time))/3600) FILTER (WHERE t.resolution_time IS NOT NULL) AS avg_resolution_hours,
+ AVG(t.customer_rating) FILTER (WHERE t.customer_rating IS NOT NULL) AS avg_rating
+FROM customer_service.tb_ticket t
+WHERE t.deleted = false
+ AND t.create_time > now() - interval '90 days'
+GROUP BY DATE(t.create_time), t.ticket_type, t.priority;
+
+COMMENT ON VIEW customer_service.v_ticket_efficiency IS '工单处理效率统计视图';
+
+-- =============================
+-- 9. 审计触发器增强
+-- =============================
+
+-- 9.1 创建审计日志函数
+CREATE OR REPLACE FUNCTION public.audit_trigger_func()
+RETURNS TRIGGER AS $$
+DECLARE
+ audit_data JSONB;
+BEGIN
+ IF (TG_OP = 'DELETE') THEN
+ audit_data := jsonb_build_object(
+ 'operation', 'DELETE',
+ 'table', TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
+ 'old_data', row_to_json(OLD)
+ );
+
+ INSERT INTO log.tb_sys_log (
+ optsn, log_id, type, level, module, message, data, creator
+ ) VALUES (
+ 'AUDIT_' || gen_random_uuid()::text,
+ gen_random_uuid()::text,
+ 'audit',
+ 'info',
+ TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
+ 'Record deleted',
+ audit_data,
+ COALESCE(current_setting('app.current_user_id', true), 'system')
+ );
+ RETURN OLD;
+
+ ELSIF (TG_OP = 'UPDATE') THEN
+ audit_data := jsonb_build_object(
+ 'operation', 'UPDATE',
+ 'table', TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
+ 'old_data', row_to_json(OLD),
+ 'new_data', row_to_json(NEW)
+ );
+
+ INSERT INTO log.tb_sys_log (
+ optsn, log_id, type, level, module, message, data, creator
+ ) VALUES (
+ 'AUDIT_' || gen_random_uuid()::text,
+ gen_random_uuid()::text,
+ 'audit',
+ 'info',
+ TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
+ 'Record updated',
+ audit_data,
+ COALESCE(current_setting('app.current_user_id', true), 'system')
+ );
+ RETURN NEW;
+
+ ELSIF (TG_OP = 'INSERT') THEN
+ audit_data := jsonb_build_object(
+ 'operation', 'INSERT',
+ 'table', TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
+ 'new_data', row_to_json(NEW)
+ );
+
+ INSERT INTO log.tb_sys_log (
+ optsn, log_id, type, level, module, message, data, creator
+ ) VALUES (
+ 'AUDIT_' || gen_random_uuid()::text,
+ gen_random_uuid()::text,
+ 'audit',
+ 'info',
+ TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
+ 'Record created',
+ audit_data,
+ COALESCE(current_setting('app.current_user_id', true), 'system')
+ );
+ RETURN NEW;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION public.audit_trigger_func() IS '审计日志触发器函数';
+
+-- =============================
+-- 10. 数据归档函数
+-- =============================
+
+-- 10.1 创建日志归档函数
+CREATE OR REPLACE FUNCTION public.archive_old_logs(
+ p_months_to_keep INTEGER DEFAULT 6
+)
+RETURNS TABLE(
+ archived_count INTEGER,
+ deleted_count INTEGER
+) AS $$
+DECLARE
+ v_cutoff_date TIMESTAMPTZ;
+ v_archived INTEGER := 0;
+ v_deleted INTEGER := 0;
+BEGIN
+ v_cutoff_date := now() - (p_months_to_keep || ' months')::INTERVAL;
+
+ -- 归档系统日志
+ CREATE TABLE IF NOT EXISTS log.tb_sys_log_archived (LIKE log.tb_sys_log INCLUDING ALL);
+
+ WITH moved_rows AS (
+ INSERT INTO log.tb_sys_log_archived
+ SELECT * FROM log.tb_sys_log
+ WHERE create_time < v_cutoff_date
+ RETURNING *
+ )
+ SELECT COUNT(*) INTO v_archived FROM moved_rows;
+
+ DELETE FROM log.tb_sys_log
+ WHERE create_time < v_cutoff_date;
+
+ GET DIAGNOSTICS v_deleted = ROW_COUNT;
+
+ RETURN QUERY SELECT v_archived, v_deleted;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION public.archive_old_logs IS '归档旧日志数据';
+
+-- 10.2 创建API调用日志归档函数
+CREATE OR REPLACE FUNCTION agent.archive_api_logs(
+ p_months_to_keep INTEGER DEFAULT 3
+)
+RETURNS INTEGER AS $$
+DECLARE
+ v_cutoff_date TIMESTAMPTZ;
+ v_archived INTEGER;
+BEGIN
+ v_cutoff_date := now() - (p_months_to_keep || ' months')::INTERVAL;
+
+ CREATE TABLE IF NOT EXISTS agent.tb_api_call_log_archived (LIKE agent.tb_api_call_log INCLUDING ALL);
+
+ WITH moved_rows AS (
+ INSERT INTO agent.tb_api_call_log_archived
+ SELECT * FROM agent.tb_api_call_log
+ WHERE create_time < v_cutoff_date
+ RETURNING *
+ )
+ SELECT COUNT(*) INTO v_archived FROM moved_rows;
+
+ DELETE FROM agent.tb_api_call_log
+ WHERE create_time < v_cutoff_date;
+
+ RETURN v_archived;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION agent.archive_api_logs IS '归档旧API调用日志';
+
+-- =============================
+-- 11. 性能监控函数
+-- =============================
+
+-- 11.1 表膨胀检查函数
+CREATE OR REPLACE FUNCTION public.check_table_bloat()
+RETURNS TABLE(
+ schemaname TEXT,
+ tablename TEXT,
+ table_size_mb NUMERIC,
+ bloat_size_mb NUMERIC,
+ bloat_ratio NUMERIC
+) AS $$
+BEGIN
+ RETURN QUERY
+ SELECT
+ s.schemaname::TEXT,
+ s.tablename::TEXT,
+ ROUND(pg_total_relation_size(s.schemaname || '.' || s.tablename)::NUMERIC / 1024 / 1024, 2) AS table_size_mb,
+ ROUND((pg_total_relation_size(s.schemaname || '.' || s.tablename) - pg_relation_size(s.schemaname || '.' || s.tablename))::NUMERIC / 1024 / 1024, 2) AS bloat_size_mb,
+ ROUND(((pg_total_relation_size(s.schemaname || '.' || s.tablename) - pg_relation_size(s.schemaname || '.' || s.tablename))::NUMERIC / NULLIF(pg_total_relation_size(s.schemaname || '.' || s.tablename), 0) * 100), 2) AS bloat_ratio
+ FROM pg_tables s
+ WHERE s.schemaname IN ('sys', 'file', 'message', 'log', 'config', 'knowledge', 'bidding', 'customer_service', 'agent')
+ ORDER BY pg_total_relation_size(s.schemaname || '.' || s.tablename) DESC;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION public.check_table_bloat IS '检查表膨胀情况';
+
+-- =============================
+-- 执行完成提示
+-- =============================
+DO $$
+BEGIN
+ RAISE NOTICE '==============================';
+ RAISE NOTICE '数据库优化补丁执行完成!';
+ RAISE NOTICE '==============================';
+ RAISE NOTICE '已完成以下优化:';
+ RAISE NOTICE '1. 用户模块:移除敏感字段、添加部门关联';
+ RAISE NOTICE '2. 权限模块:添加排序和类型字段';
+ RAISE NOTICE '3. 文件模块:版本管理、分类标签、关联表';
+ RAISE NOTICE '4. 消息模块:消息模板表';
+ RAISE NOTICE '5. 日志模块:链路追踪支持';
+ RAISE NOTICE '6. 全文搜索:添加GIN索引';
+ RAISE NOTICE '7. JSONB优化:添加GIN索引';
+ RAISE NOTICE '8. 性能视图:用户权限、智能体状态、工单效率';
+ RAISE NOTICE '9. 审计增强:完整审计触发器';
+ RAISE NOTICE '10. 数据归档:日志和API调用归档函数';
+ RAISE NOTICE '11. 监控工具:表膨胀检查函数';
+ RAISE NOTICE '==============================';
+END $$;
diff --git a/urbanLifelineServ/.bin/database/redis/redis.yaml b/urbanLifelineServ/.bin/database/redis/redis.yaml
new file mode 100644
index 0000000..6c0f57c
--- /dev/null
+++ b/urbanLifelineServ/.bin/database/redis/redis.yaml
@@ -0,0 +1,137 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: redis-config
+ namespace: urban-lifeline
+data:
+ redis.conf: |
+ bind 0.0.0.0
+ protected-mode no
+ port 6379
+ tcp-backlog 511
+ timeout 0
+ tcp-keepalive 300
+ daemonize no
+ supervised no
+ pidfile /var/run/redis_6379.pid
+ loglevel notice
+ logfile ""
+ databases 16
+ always-show-logo yes
+ save 900 1
+ save 300 10
+ save 60 10000
+ stop-writes-on-bgsave-error yes
+ rdbcompression yes
+ rdbchecksum yes
+ dbfilename dump.rdb
+ dir /data
+ maxmemory 512mb
+ maxmemory-policy allkeys-lru
+ appendonly yes
+ appendfilename "appendonly.aof"
+ appendfsync everysec
+ no-appendfsync-on-rewrite no
+ auto-aof-rewrite-percentage 100
+ auto-aof-rewrite-min-size 64mb
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: redis
+ namespace: urban-lifeline
+ labels:
+ app: redis
+spec:
+ selector:
+ app: redis
+ ports:
+ - name: redis
+ port: 6379
+ targetPort: 6379
+ type: ClusterIP
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: redis-nodeport
+ namespace: urban-lifeline
+ labels:
+ app: redis
+spec:
+ selector:
+ app: redis
+ type: NodePort
+ ports:
+ - name: redis
+ port: 6379
+ targetPort: 6379
+ nodePort: 30379
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: redis
+ namespace: urban-lifeline
+ labels:
+ app: redis
+spec:
+ serviceName: redis
+ replicas: 1
+ selector:
+ matchLabels:
+ app: redis
+ template:
+ metadata:
+ labels:
+ app: redis
+ spec:
+ containers:
+ - name: redis
+ image: redis:7-alpine
+ imagePullPolicy: IfNotPresent
+ command:
+ - redis-server
+ - /usr/local/etc/redis/redis.conf
+ ports:
+ - containerPort: 6379
+ name: redis
+ env:
+ - name: TZ
+ value: "Asia/Shanghai"
+ volumeMounts:
+ - name: redis-data
+ mountPath: /data
+ - name: redis-config
+ mountPath: /usr/local/etc/redis
+ livenessProbe:
+ exec:
+ command:
+ - redis-cli
+ - ping
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ timeoutSeconds: 5
+ readinessProbe:
+ exec:
+ command:
+ - redis-cli
+ - ping
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ timeoutSeconds: 5
+ resources:
+ requests:
+ memory: "128Mi"
+ cpu: "100m"
+ limits:
+ memory: "512Mi"
+ cpu: "500m"
+ volumes:
+ - name: redis-data
+ hostPath:
+ path: /data/k8s/redis/data
+ type: DirectoryOrCreate
+ - name: redis-config
+ configMap:
+ name: redis-config
diff --git a/urbanLifelineServ/.bin/docker/docker-compose.yml b/urbanLifelineServ/.bin/docker/docker-compose.yml
new file mode 100644
index 0000000..2dd32bc
--- /dev/null
+++ b/urbanLifelineServ/.bin/docker/docker-compose.yml
@@ -0,0 +1,62 @@
+version: '3.8'
+
+# urban-lifeline 开发环境 Docker Compose 配置
+# 使用主机的 MySQL 数据库
+
+networks:
+ urban-lifeline:
+ driver: bridge
+ name: urban-lifeline
+
+services:
+ nacos:
+ image: nacos/nacos-server:v3.1.0
+ container_name: urban-lifeline-nacos
+ restart: unless-stopped
+ networks:
+ - urban-lifeline
+ ports:
+ - "8081:8080" # Nacos Console (Web UI) - 映射到主机 8081
+ - "8848:8848" # Nacos HTTP API
+ - "9848:9848" # Nacos gRPC 客户端请求
+ - "9849:9849" # Nacos gRPC 服务间同步
+ environment:
+ # 运行模式
+ MODE: standalone
+
+ # 数据库配置 - 使用主机 MySQL
+ SPRING_DATASOURCE_PLATFORM: mysql
+ MYSQL_SERVICE_HOST: host.docker.internal # Docker Desktop
+ # MYSQL_SERVICE_HOST: 172.17.0.1 # Linux 使用此行,注释上一行
+ MYSQL_SERVICE_PORT: 3306
+ MYSQL_SERVICE_DB_NAME: nacos_config
+ MYSQL_SERVICE_USER: root
+ MYSQL_SERVICE_PASSWORD: "123456"
+ MYSQL_SERVICE_DB_PARAM: allowPublicKeyRetrieval=true&useSSL=false
+
+ # JVM 配置
+ JVM_XMS: 512m
+ JVM_XMX: 512m
+ JVM_XMN: 256m
+
+ # 认证配置(开发环境关闭)
+ NACOS_AUTH_ENABLE: "false"
+ NACOS_AUTH_TOKEN: ZlRkR2ZxR3BvZ1F0a3JxY2V6RUx2cUh1Rkx6V1ZQbE9kUVd1R1VOcWFFS2t3dG5hS0E9PQ==
+ NACOS_AUTH_IDENTITY_KEY: ZlRkR2ZxR3BvZ1F0a3JxY2V6RUx2cUh1Rkx6V1ZQbE9kUVd1R1VOcWFFS2t3dG5hS0E9PQ==
+ NACOS_AUTH_IDENTITY_VALUE: ZlRkR2ZxR3BvZ1F0a3JxY2V6RUx2cUh1Rkx6V1ZQbE9kUVd1R1VOcWFFS2t3dG5hS0E9PQ==
+
+ volumes:
+ # 数据持久化到主机目录
+ - ../../.data/docker/nacos/data:/home/nacos/data
+ - ../../.data/docker/nacos/logs:/home/nacos/logs
+
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:8848/nacos/"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+ start_period: 60s
+
+ # Linux 需要添加 extra_hosts 来访问主机服务
+ extra_hosts:
+ - "host.docker.internal:host-gateway"
diff --git a/urbanLifelineServ/.bin/docker/readme.md b/urbanLifelineServ/.bin/docker/readme.md
new file mode 100644
index 0000000..dfb38d9
--- /dev/null
+++ b/urbanLifelineServ/.bin/docker/readme.md
@@ -0,0 +1,3 @@
+# 注意点
+1. mysql 开发 bind-address 必须是 0.0.0.0 否则无法连接
+2. root % 密码必须是 123456
\ No newline at end of file
diff --git a/urbanLifelineServ/.bin/docker/start.sh b/urbanLifelineServ/.bin/docker/start.sh
new file mode 100644
index 0000000..d2b085d
--- /dev/null
+++ b/urbanLifelineServ/.bin/docker/start.sh
@@ -0,0 +1,210 @@
+#!/bin/bash
+
+# urban-lifeline Docker Compose 启动脚本
+
+set -e
+
+# If the user invoked this script with `sh start.sh` (which may be dash),
+# some Bash-specific features (like ${BASH_SOURCE[0]} and [[ .. ]]) will
+# fail with "Bad substitution". If we're not running under Bash, try to
+# re-exec the script using `bash` if available.
+if [ -z "$BASH_VERSION" ]; then
+ if command -v bash >/dev/null 2>&1; then
+ echo "脚本需要 Bash,正在重新以 Bash 执行..."
+ exec bash "$0" "$@"
+ else
+ echo "错误: 需要 Bash 运行此脚本,但系统未安装 Bash。"
+ exit 1
+ fi
+fi
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+DATA_DIR="$PROJECT_ROOT/.data/docker"
+
+echo "=========================================="
+echo "urban-lifeline Docker 开发环境启动"
+echo "=========================================="
+echo "项目目录: $PROJECT_ROOT"
+echo "数据目录: $DATA_DIR"
+echo ""
+
+# 1. 检查 Docker
+echo "1. 检查 Docker..."
+if ! command -v docker &> /dev/null; then
+ echo "❌ 错误: 未找到 Docker"
+ echo "请先安装 Docker: https://docs.docker.com/get-docker/"
+ exit 1
+fi
+
+if ! docker info &> /dev/null; then
+ echo "❌ 错误: Docker 未运行"
+ echo "请启动 Docker 服务"
+ exit 1
+fi
+echo "✓ Docker 检查完成"
+echo ""
+
+# 2. 检查 Docker Compose
+echo "2. 检查 Docker Compose..."
+if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
+ echo "❌ 错误: 未找到 Docker Compose"
+ echo "请先安装 Docker Compose: https://docs.docker.com/compose/install/"
+ exit 1
+fi
+echo "✓ Docker Compose 检查完成"
+echo ""
+
+# 3. 检查主机 MySQL
+echo "3. 检查主机 MySQL..."
+if command -v mysql &> /dev/null; then
+ echo "请输入 MySQL 连接信息 (按 Enter 使用默认值):"
+ read -p " 主机 [127.0.0.1]: " MYSQL_HOST
+ MYSQL_HOST=${MYSQL_HOST:-127.0.0.1}
+
+ read -p " 端口 [3306]: " MYSQL_PORT
+ MYSQL_PORT=${MYSQL_PORT:-3306}
+
+ read -p " 用户名 [root]: " MYSQL_USER
+ MYSQL_USER=${MYSQL_USER:-root}
+
+ read -s -p " 密码 [123456]: " MYSQL_PASSWORD
+ echo
+ MYSQL_PASSWORD=${MYSQL_PASSWORD:-123456}
+
+ read -p " 数据库名 [nacos_config]: " MYSQL_DB
+ MYSQL_DB=${MYSQL_DB:-nacos_config}
+
+ echo ""
+ echo "测试 MySQL 连接..."
+ if mysql -h"$MYSQL_HOST" -P"$MYSQL_PORT" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "USE $MYSQL_DB;" 2>/dev/null; then
+ echo "✓ MySQL 连接成功"
+ else
+ echo "⚠️ 警告: MySQL 连接失败"
+ echo ""
+ echo "请确保:"
+ echo " 1. MySQL 服务正在运行"
+ echo " 2. 数据库 '$MYSQL_DB' 已创建"
+ echo " 3. 用户 '$MYSQL_USER' 有权限访问该数据库"
+ echo ""
+ read -p "是否继续启动? (y/N): " -n 1 -r
+ echo
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ exit 1
+ fi
+ fi
+else
+ echo "⚠️ 警告: 未找到 mysql 客户端,无法验证数据库连接"
+ echo "将使用默认配置: root:123456@localhost:3306/nacos_config"
+ MYSQL_USER=root
+ MYSQL_PASSWORD=123456
+ MYSQL_DB=nacos_config
+fi
+echo ""
+
+# 4. 创建数据目录
+echo "4. 创建数据目录..."
+mkdir -p "$DATA_DIR"/nacos/{data,logs}
+echo "✓ 数据目录创建完成"
+echo ""
+
+# 5. 更新 docker-compose.yml 配置
+if [ -n "$MYSQL_USER" ] && [ "$MYSQL_USER" != "root" ]; then
+ echo "5. 更新 Docker Compose 配置..."
+
+ # 创建临时 .env 文件
+ cat > "$SCRIPT_DIR/.env" < /dev/null; then
+ docker-compose pull
+else
+ docker compose pull
+fi
+echo "✓ 镜像拉取完成"
+echo ""
+
+# 7. 启动服务
+echo "7. 启动服务..."
+if command -v docker-compose &> /dev/null; then
+ docker-compose up -d
+else
+ docker compose up -d
+fi
+echo "✓ 服务启动完成"
+echo ""
+
+# 8. 等待 Nacos 启动
+echo "8. 等待 Nacos 启动..."
+echo " 这可能需要 30-60 秒..."
+
+MAX_WAIT=60
+WAITED=0
+while [ $WAITED -lt $MAX_WAIT ]; do
+ if curl -s http://localhost:8848/nacos/ > /dev/null 2>&1; then
+ echo "✓ Nacos 启动成功"
+ break
+ fi
+ sleep 2
+ WAITED=$((WAITED + 2))
+ echo -n "."
+done
+echo ""
+
+if [ $WAITED -ge $MAX_WAIT ]; then
+ echo "⚠️ 警告: Nacos 启动超时"
+ echo ""
+ echo "查看日志:"
+ if command -v docker-compose &> /dev/null; then
+ docker-compose logs nacos
+ else
+ docker compose logs nacos
+ fi
+ echo ""
+ echo "请检查 MySQL 连接配置是否正确"
+ exit 1
+fi
+
+# 9. 显示服务信息
+echo ""
+echo "=========================================="
+echo "✅ urban-lifeline 开发环境启动完成!"
+echo "=========================================="
+echo ""
+echo "服务访问地址:"
+echo " - Nacos 控制台: http://localhost:8848/nacos/"
+echo " - Nacos 默认账号: nacos / nacos"
+echo ""
+echo "数据库连接信息:"
+echo " - 数据库: ${MYSQL_DB:-nacos_config}"
+echo " - 用户: ${MYSQL_USER:-root}"
+echo ""
+echo "数据持久化目录:"
+echo " - $DATA_DIR/nacos/data"
+echo " - $DATA_DIR/nacos/logs"
+echo ""
+echo "常用命令:"
+echo " 查看运行状态:"
+echo " docker ps"
+echo ""
+echo " 查看 Nacos 日志:"
+echo " docker logs -f urban-lifeline-nacos"
+echo ""
+echo " 停止服务:"
+echo " cd $SCRIPT_DIR && docker-compose down"
+echo ""
+echo " 重启服务:"
+echo " cd $SCRIPT_DIR && docker-compose restart"
+echo ""
+echo " 完全清理(删除容器和网络):"
+echo " cd $SCRIPT_DIR && docker-compose down -v"
+echo ""
diff --git a/urbanLifelineServ/.bin/docker/stop.sh b/urbanLifelineServ/.bin/docker/stop.sh
new file mode 100644
index 0000000..0c97407
--- /dev/null
+++ b/urbanLifelineServ/.bin/docker/stop.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# urban-lifeline Docker Compose 停止脚本
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+echo "=========================================="
+echo "停止 urban-lifeline 开发环境"
+echo "=========================================="
+echo ""
+
+cd "$SCRIPT_DIR"
+
+if command -v docker-compose &> /dev/null; then
+ docker-compose down
+else
+ docker compose down
+fi
+
+echo ""
+echo "✅ 服务已停止"
+echo ""
+echo "数据已保留在: ../../.data/docker/nacos/"
+echo ""
+echo "如需重新启动,请运行: ./start.sh"
+echo "如需完全清理,请运行: docker-compose down -v"
+echo ""
diff --git a/urbanLifelineServ/.bin/sql/nacos/nacos-mysql-schema.sql b/urbanLifelineServ/.bin/sql/nacos/nacos-mysql-schema.sql
new file mode 100644
index 0000000..2bc64da
--- /dev/null
+++ b/urbanLifelineServ/.bin/sql/nacos/nacos-mysql-schema.sql
@@ -0,0 +1,182 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+DROP DATABASE IF EXISTS `nacos_config`;
+CREATE DATABASE IF NOT EXISTS `nacos_config` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
+USE `nacos_config`;
+
+/******************************************/
+/* 表名称 = config_info */
+/******************************************/
+CREATE TABLE `config_info` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `data_id` varchar(255) NOT NULL COMMENT 'data_id',
+ `group_id` varchar(128) DEFAULT NULL COMMENT 'group_id',
+ `content` longtext NOT NULL COMMENT 'content',
+ `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
+ `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
+ `src_user` text COMMENT 'source user',
+ `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
+ `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
+ `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
+ `c_desc` varchar(256) DEFAULT NULL COMMENT 'configuration description',
+ `c_use` varchar(64) DEFAULT NULL COMMENT 'configuration usage',
+ `effect` varchar(64) DEFAULT NULL COMMENT '配置生效的描述',
+ `type` varchar(64) DEFAULT NULL COMMENT '配置的类型',
+ `c_schema` text COMMENT '配置的模式',
+ `encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
+
+/******************************************/
+/* 表名称 = config_info since 2.5.0 */
+/******************************************/
+CREATE TABLE `config_info_gray` (
+ `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `data_id` varchar(255) NOT NULL COMMENT 'data_id',
+ `group_id` varchar(128) NOT NULL COMMENT 'group_id',
+ `content` longtext NOT NULL COMMENT 'content',
+ `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
+ `src_user` text COMMENT 'src_user',
+ `src_ip` varchar(100) DEFAULT NULL COMMENT 'src_ip',
+ `gmt_create` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_create',
+ `gmt_modified` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_modified',
+ `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
+ `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
+ `gray_name` varchar(128) NOT NULL COMMENT 'gray_name',
+ `gray_rule` text NOT NULL COMMENT 'gray_rule',
+ `encrypted_data_key` varchar(256) NOT NULL DEFAULT '' COMMENT 'encrypted_data_key',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_configinfogray_datagrouptenantgray` (`data_id`,`group_id`,`tenant_id`,`gray_name`),
+ KEY `idx_dataid_gmt_modified` (`data_id`,`gmt_modified`),
+ KEY `idx_gmt_modified` (`gmt_modified`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='config_info_gray';
+
+/******************************************/
+/* 表名称 = config_tags_relation */
+/******************************************/
+CREATE TABLE `config_tags_relation` (
+ `id` bigint(20) NOT NULL COMMENT 'id',
+ `tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
+ `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
+ `data_id` varchar(255) NOT NULL COMMENT 'data_id',
+ `group_id` varchar(128) NOT NULL COMMENT 'group_id',
+ `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
+ `nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识',
+ PRIMARY KEY (`nid`),
+ UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
+ KEY `idx_tenant_id` (`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
+
+/******************************************/
+/* 表名称 = group_capacity */
+/******************************************/
+CREATE TABLE `group_capacity` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+ `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
+ `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
+ `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
+ `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
+ `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
+ `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
+ `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
+ `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_group_id` (`group_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
+
+/******************************************/
+/* 表名称 = his_config_info */
+/******************************************/
+CREATE TABLE `his_config_info` (
+ `id` bigint(20) unsigned NOT NULL COMMENT 'id',
+ `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识',
+ `data_id` varchar(255) NOT NULL COMMENT 'data_id',
+ `group_id` varchar(128) NOT NULL COMMENT 'group_id',
+ `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
+ `content` longtext NOT NULL COMMENT 'content',
+ `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
+ `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
+ `src_user` text COMMENT 'source user',
+ `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
+ `op_type` char(10) DEFAULT NULL COMMENT 'operation type',
+ `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
+ `encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥',
+ `publish_type` varchar(50) DEFAULT 'formal' COMMENT 'publish type gray or formal',
+ `gray_name` varchar(50) DEFAULT NULL COMMENT 'gray name',
+ `ext_info` longtext DEFAULT NULL COMMENT 'ext info',
+ PRIMARY KEY (`nid`),
+ KEY `idx_gmt_create` (`gmt_create`),
+ KEY `idx_gmt_modified` (`gmt_modified`),
+ KEY `idx_did` (`data_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
+
+
+/******************************************/
+/* 表名称 = tenant_capacity */
+/******************************************/
+CREATE TABLE `tenant_capacity` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+ `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
+ `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
+ `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
+ `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
+ `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
+ `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
+ `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
+ `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_tenant_id` (`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
+
+
+CREATE TABLE `tenant_info` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `kp` varchar(128) NOT NULL COMMENT 'kp',
+ `tenant_id` varchar(128) default '' COMMENT 'tenant_id',
+ `tenant_name` varchar(128) default '' COMMENT 'tenant_name',
+ `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
+ `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
+ `gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
+ `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
+ KEY `idx_tenant_id` (`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
+
+CREATE TABLE `users` (
+ `username` varchar(50) NOT NULL PRIMARY KEY COMMENT 'username',
+ `password` varchar(500) NOT NULL COMMENT 'password',
+ `enabled` boolean NOT NULL COMMENT 'enabled'
+);
+
+CREATE TABLE `roles` (
+ `username` varchar(50) NOT NULL COMMENT 'username',
+ `role` varchar(50) NOT NULL COMMENT 'role',
+ UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
+);
+
+CREATE TABLE `permissions` (
+ `role` varchar(50) NOT NULL COMMENT 'role',
+ `resource` varchar(128) NOT NULL COMMENT 'resource',
+ `action` varchar(8) NOT NULL COMMENT 'action',
+ UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
+);
+
diff --git a/urbanLifelineServ/.gitignore b/urbanLifelineServ/.gitignore
new file mode 100644
index 0000000..7729d81
--- /dev/null
+++ b/urbanLifelineServ/.gitignore
@@ -0,0 +1,27 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+.data
+.idea
+*/target
+*/*/target
\ No newline at end of file
diff --git a/urbanLifelineServ/.vscode/README-snippets.md b/urbanLifelineServ/.vscode/README-snippets.md
new file mode 100644
index 0000000..bc7ba33
--- /dev/null
+++ b/urbanLifelineServ/.vscode/README-snippets.md
@@ -0,0 +1,88 @@
+# MyBatis XML 代码片段使用说明
+
+## 安装位置
+
+代码片段文件已放置在 `.vscode/mybatis-xml.code-snippets`,VS Code 和 Cursor 会自动识别。
+
+## 使用方法
+
+### 1. 完整 Mapper XML 模板
+
+在 XML 文件中输入 `mybatis-mapper`,然后按 `Tab` 键,会自动生成完整的 MyBatis Mapper XML 模板。
+
+**占位符说明:**
+- `${1}` - Mapper 命名空间(完整包路径)
+- `${2}` - 模块名称(如:user, role, dept)
+- `${3}` - 实体类名称(如:TbSysUser)
+- `${4}` - DTO 完整类路径
+- `${5}` - 主键数据库字段名(如:user_id)
+- `${6}` - 主键 Java 属性名(如:userId)
+- `${7}` - 业务字段数据库字段名
+- `${8}` - 业务字段 Java 属性名
+- `${9}` - 实体中文名称(如:用户)
+- `${10}` - 数据库表名(如:tb_sys_user)
+
+### 2. 单个 SQL 片段
+
+#### ResultMap
+输入 `mybatis-resultmap` 生成结果映射
+
+#### Insert
+输入 `mybatis-insert` 生成插入语句
+
+#### Update
+输入 `mybatis-update` 生成更新语句
+
+#### Delete
+输入 `mybatis-delete` 生成删除语句(逻辑删除)
+
+#### Select ById
+输入 `mybatis-select-id` 生成根据ID查询
+
+#### Select ByFilter
+输入 `mybatis-select-filter` 生成条件查询
+
+#### Select Page
+输入 `mybatis-select-page` 生成分页查询
+
+#### Select Count
+输入 `mybatis-select-count` 生成计数查询
+
+#### Base Column List
+输入 `mybatis-columns` 生成基础列定义
+
+## 示例
+
+### 创建完整的 Mapper XML
+
+1. 新建文件:`TbSysRoleMapper.xml`
+2. 输入 `mybatis-mapper` 并按 `Tab`
+3. 依次填写占位符:
+ - 命名空间:`org.xyzh.system.mapper.role.TbSysRoleMapper`
+ - 模块名:`role`
+ - 实体名:`TbSysRole`
+ - DTO路径:`org.xyzh.common.dto.sys.TbSysRoleDTO`
+ - 主键字段:`role_id`
+ - 主键属性:`roleId`
+ - 业务字段:`role_name`
+ - 业务属性:`roleName`
+ - 实体中文名:`角色`
+ - 表名:`tb_sys_role`
+
+### 快速添加单个 SQL
+
+在已有的 Mapper XML 中,输入对应的前缀(如 `mybatis-insert`),按 `Tab` 即可快速插入对应的 SQL 片段。
+
+## 注意事项
+
+1. 所有模板都包含了 BaseDTO 的通用字段
+2. 删除操作使用逻辑删除(设置 deleted = true)
+3. 查询时自动过滤已删除的记录(deleted = false)
+4. 分页使用 LIMIT 和 OFFSET(PostgreSQL/MySQL 兼容)
+5. 时间字段使用 TIMESTAMP 类型
+6. 字符串字段使用 VARCHAR 类型
+
+## 自定义
+
+如需修改模板,编辑 `.vscode/mybatis-xml.code-snippets` 文件即可。
+
diff --git a/urbanLifelineServ/.vscode/launch.json b/urbanLifelineServ/.vscode/launch.json
new file mode 100644
index 0000000..a63fd42
--- /dev/null
+++ b/urbanLifelineServ/.vscode/launch.json
@@ -0,0 +1,23 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "java",
+ "name": "SystemApp",
+ "request": "launch",
+ "mainClass": "org.xyzh.SystemApp",
+ "projectName": "system",
+ "cwd": "${workspaceFolder}/system",
+ "args": [
+ "--spring.profiles.active=dev"
+ ],
+ "vmArgs": [
+ "-Dspring.profiles.active=dev"
+ ],
+ "env": {
+ "SPRING_PROFILES_ACTIVE": "dev"
+ },
+ "console": "integratedTerminal"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/urbanLifelineServ/.vscode/mybatis-xml.code-snippets b/urbanLifelineServ/.vscode/mybatis-xml.code-snippets
new file mode 100644
index 0000000..9981239
--- /dev/null
+++ b/urbanLifelineServ/.vscode/mybatis-xml.code-snippets
@@ -0,0 +1,313 @@
+{
+ "MyBatis Mapper XML Template": {
+ "prefix": "mybatis-mapper",
+ "body": [
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ "",
+ " ",
+ " ",
+ " ${5:entity_id}, ${7:field_name},",
+ " optsn, creator, updater, dept_path, remark, create_time, update_time, delete_time, deleted",
+ " ",
+ "",
+ " ",
+ " ",
+ " INSERT INTO ${10:tb_entity_name} (",
+ " ${5:entity_id}, ${7:field_name},",
+ " optsn, creator, updater, dept_path, remark, create_time, update_time, deleted",
+ " ) VALUES (",
+ " #{${6:entityId}}, #{${8:fieldName}},",
+ " #{optsn}, #{creator}, #{updater}, #{deptPath}, #{remark}, #{createTime}, #{updateTime}, #{deleted}",
+ " )",
+ " ",
+ "",
+ " ",
+ " ",
+ " UPDATE ${10:tb_entity_name}",
+ " ",
+ " ",
+ " ${7:field_name} = #{${8:fieldName}},",
+ " ",
+ " ",
+ " updater = #{updater},",
+ " ",
+ " ",
+ " dept_path = #{deptPath},",
+ " ",
+ " ",
+ " remark = #{remark},",
+ " ",
+ " ",
+ " update_time = #{updateTime},",
+ " ",
+ " ",
+ " WHERE ${5:entity_id} = #{${6:entityId}}",
+ " ",
+ " AND deleted = #{deleted}",
+ " ",
+ " ",
+ "",
+ " ",
+ " ",
+ " UPDATE ${10:tb_entity_name}",
+ " SET deleted = true,",
+ " delete_time = NOW()",
+ " WHERE ${5:entity_id} = #{${6:entityId}}",
+ " ",
+ "",
+ " ",
+ " ",
+ "",
+ " ",
+ " ",
+ "",
+ " ",
+ " ",
+ "",
+ " ",
+ " ",
+ "",
+ ""
+ ],
+ "description": "MyBatis Mapper XML 完整模板"
+ },
+ "MyBatis ResultMap": {
+ "prefix": "mybatis-resultmap",
+ "body": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "description": "MyBatis ResultMap 映射"
+ },
+ "MyBatis Insert": {
+ "prefix": "mybatis-insert",
+ "body": [
+ " ",
+ " ",
+ " INSERT INTO ${4:tb_entity_name} (",
+ " ${5:entity_id}, ${6:field_name},",
+ " optsn, creator, updater, dept_path, remark, create_time, update_time, deleted",
+ " ) VALUES (",
+ " #{${7:entityId}}, #{${8:fieldName}},",
+ " #{optsn}, #{creator}, #{updater}, #{deptPath}, #{remark}, #{createTime}, #{updateTime}, #{deleted}",
+ " )",
+ " "
+ ],
+ "description": "MyBatis Insert 语句"
+ },
+ "MyBatis Update": {
+ "prefix": "mybatis-update",
+ "body": [
+ " ",
+ " ",
+ " UPDATE ${4:tb_entity_name}",
+ " ",
+ " ",
+ " ${6:field_name} = #{${5:fieldName}},",
+ " ",
+ " ",
+ " updater = #{updater},",
+ " ",
+ " ",
+ " dept_path = #{deptPath},",
+ " ",
+ " ",
+ " remark = #{remark},",
+ " ",
+ " ",
+ " update_time = #{updateTime},",
+ " ",
+ " ",
+ " WHERE ${7:entity_id} = #{${8:entityId}}",
+ " ",
+ " AND deleted = #{deleted}",
+ " ",
+ " "
+ ],
+ "description": "MyBatis Update 语句"
+ },
+ "MyBatis Delete": {
+ "prefix": "mybatis-delete",
+ "body": [
+ " ",
+ " ",
+ " UPDATE ${4:tb_entity_name}",
+ " SET deleted = true,",
+ " delete_time = NOW()",
+ " WHERE ${5:entity_id} = #{${6:entityId}}",
+ " "
+ ],
+ "description": "MyBatis Delete 语句(逻辑删除)"
+ },
+ "MyBatis Select ById": {
+ "prefix": "mybatis-select-id",
+ "body": [
+ " ",
+ " "
+ ],
+ "description": "MyBatis Select ById 查询"
+ },
+ "MyBatis Select ByFilter": {
+ "prefix": "mybatis-select-filter",
+ "body": [
+ " ",
+ " "
+ ],
+ "description": "MyBatis Select ByFilter 查询"
+ },
+ "MyBatis Select Page": {
+ "prefix": "mybatis-select-page",
+ "body": [
+ " ",
+ " "
+ ],
+ "description": "MyBatis Select Page 分页查询"
+ },
+ "MyBatis Select Count": {
+ "prefix": "mybatis-select-count",
+ "body": [
+ " ",
+ " "
+ ],
+ "description": "MyBatis Select Count 计数查询"
+ },
+ "MyBatis Base Column List": {
+ "prefix": "mybatis-columns",
+ "body": [
+ " ",
+ " ",
+ " ${1:entity_id}, ${2:field_name},",
+ " optsn, creator, updater, dept_path, remark, create_time, update_time, delete_time, deleted",
+ " "
+ ],
+ "description": "MyBatis 基础列定义"
+ }
+}
+
diff --git a/urbanLifelineServ/.vscode/settings.json b/urbanLifelineServ/.vscode/settings.json
new file mode 100644
index 0000000..5e320b2
--- /dev/null
+++ b/urbanLifelineServ/.vscode/settings.json
@@ -0,0 +1,15 @@
+{
+ "java.compile.nullAnalysis.mode": "automatic",
+ "java.configuration.updateBuildConfiguration": "automatic",
+ "maven.view": "hierarchical",
+ "tabSize": 4,
+ "java.debug.settings.onBuildFailureProceed": true,
+ "java.configuration.maven.userSettings": null,
+ "java.import.maven.enabled": true,
+ "java.project.referencedLibraries": [
+ "**/*.jar"
+ ],
+ "maven.terminal.useJavaHome": true,
+ "java.configuration.runtimes": [],
+ "Codegeex.RepoIndex": true
+}
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-all/pom.xml b/urbanLifelineServ/apis/api-all/pom.xml
new file mode 100644
index 0000000..e52af95
--- /dev/null
+++ b/urbanLifelineServ/apis/api-all/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ apis
+ 1.0.0
+
+
+ org.xyzh.apis
+ api-all
+ ${urban-lifeline.version}
+ jar
+
+
+ 21
+ 21
+
+
+
+
+ org.xyzh.apis
+ api-auth
+
+
+ org.xyzh.apis
+ api-file
+
+
+ org.xyzh.apis
+ api-message
+
+
+ org.xyzh.apis
+ api-system
+
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-auth/pom.xml b/urbanLifelineServ/apis/api-auth/pom.xml
new file mode 100644
index 0000000..8fc7dfc
--- /dev/null
+++ b/urbanLifelineServ/apis/api-auth/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ apis
+ 1.0.0
+
+
+ org.xyzh.apis
+ api-auth
+ ${urban-lifeline.version}
+ jar
+
+
+ 21
+ 21
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-auth/src/main/java/org/xyzh/api/auth/service/AuthService.java b/urbanLifelineServ/apis/api-auth/src/main/java/org/xyzh/api/auth/service/AuthService.java
new file mode 100644
index 0000000..87a2bec
--- /dev/null
+++ b/urbanLifelineServ/apis/api-auth/src/main/java/org/xyzh/api/auth/service/AuthService.java
@@ -0,0 +1,74 @@
+package org.xyzh.api.auth.service;
+
+import org.xyzh.common.core.domain.LoginDomain;
+import org.xyzh.common.core.domain.LoginParam;
+import org.xyzh.common.core.domain.ResultDomain;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * @description 认证服务接口
+ * @filename AuthService.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-03
+ */
+public interface AuthService {
+
+ /**
+ * @description 登录
+ * @param LoginParam loginParam 登录参数
+ * @param HttpServletRequest request 请求
+ * @return ResultDomain 登录结果
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain login(LoginParam loginParam, HttpServletRequest request);
+
+ /**
+ * @description 刷新token
+ * @param HttpServletRequest request 请求
+ * @return ResultDomain 刷新token结果
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain refreshToken(HttpServletRequest request);
+
+ /**
+ * @description 登出
+ * @param HttpServletRequest request 请求
+ * @return ResultDomain 登出结果
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain logout(HttpServletRequest request);
+
+ /**
+ * @description 根据验证码类型获取验证码
+ * @param LoginParam loginParam 登录参数
+ * @return ResultDomain 获取验证码结果
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain getCaptcha(LoginParam loginParam);
+
+ /**
+ * @description 根据 token 获取登录信息,供其他服务调用以判定用户登录状态
+ * @param token 鉴权 token(例如 JWT 或会话 token)
+ * @return ResultDomain 登录信息,若 token 无效或未登录则在 ResultDomain 中返回相应状态/错误
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain getLoginByToken(String token);
+
+ /**
+ * @description 简单校验 token 是否有效(用于快速判断是否已登录)
+ * @param token 鉴权 token
+ * @return ResultDomain true 表示有效/已登录,false 表示无效/未登录
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain isTokenValid(String token);
+
+
+}
diff --git a/urbanLifelineServ/apis/api-file/pom.xml b/urbanLifelineServ/apis/api-file/pom.xml
new file mode 100644
index 0000000..3d4df96
--- /dev/null
+++ b/urbanLifelineServ/apis/api-file/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ apis
+ 1.0.0
+
+
+ org.xyzh.apis
+ api-file
+ ${urban-lifeline.version}
+ jar
+
+
+ 21
+ 21
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/dto/TbSysFileDTO.java b/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/dto/TbSysFileDTO.java
new file mode 100644
index 0000000..07d9746
--- /dev/null
+++ b/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/dto/TbSysFileDTO.java
@@ -0,0 +1,49 @@
+package org.xyzh.api.file.dto;
+
+import org.xyzh.common.dto.BaseDTO;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * @description 系统文件DTO
+ * @filename TbSysFileDTO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-04
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "系统文件DTO")
+public class TbSysFileDTO extends BaseDTO {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "文件ID (主键)")
+ private String fileId;
+
+ @Schema(description = "文件名")
+ private String name;
+
+ @Schema(description = "文件路径")
+ private String path;
+
+ @Schema(description = "文件大小(字节)")
+ private Long size;
+
+ @Schema(description = "文件类型")
+ private String type;
+
+ @Schema(description = "存储类型")
+ private String storageType;
+
+ @Schema(description = "MIME 类型")
+ private String mimeType;
+
+ @Schema(description = "文件访问 URL")
+ private String url;
+
+ @Schema(description = "文件状态")
+ private String status;
+
+}
diff --git a/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/service/FileService.java b/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/service/FileService.java
new file mode 100644
index 0000000..e806bd6
--- /dev/null
+++ b/urbanLifelineServ/apis/api-file/src/main/java/org/xyzh/api/file/service/FileService.java
@@ -0,0 +1,86 @@
+package org.xyzh.api.file.service;
+
+import org.springframework.web.multipart.MultipartFile;
+import org.xyzh.api.file.dto.TbSysFileDTO;
+import org.xyzh.common.core.domain.ResultDomain;
+
+/**
+ * @description 文件服务接口
+ * @filename FileService.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-03
+ */
+public interface FileService {
+
+ /**
+ * @description 上传文件
+ * @param file 文件对象
+ * @param module 所属模块
+ * @param businessId 业务ID
+ * @return ResultDomain 上传结果,包含文件信息
+ * @author yslg
+ * @since 2025-10-16
+ */
+ ResultDomain uploadFile(MultipartFile file, String module, String businessId);
+
+ /**
+ * @description 批量上传文件
+ * @param files 文件对象列表
+ * @param module 所属模块
+ * @param businessId 业务ID
+ * @param uploader 上传者用户ID(可选)
+ * @return ResultDomain 上传结果,包含文件信息列表
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain batchUploadFiles(MultipartFile[] files, String module, String businessId, String uploader);
+
+ /**
+ * @description 删除文件
+ * @param fileId 文件ID
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain deleteFile(String fileId);
+
+ /**
+ * @description 批量删除文件(逻辑删除)
+ * @param fileIds 文件ID列表
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-10-16
+ */
+ ResultDomain batchDeleteFiles(String[] fileIds);
+
+ /**
+ * @description 下载文件
+ * @param fileId 文件ID
+ * @return ResultDomain 文件字节数组
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain downloadFile(String fileId);
+
+ /**
+ * @description 根据文件ID查询文件信息
+ * @param fileId 文件ID
+ * @return ResultDomain 文件信息
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain getFileById(String fileId);
+
+ /**
+ * @description 保存临时文件
+ * @param file 文件对象
+ * @param module 所属模块
+ * @param businessId 业务ID
+ * @return ResultDomain 保存结果
+ * @author yslg
+ * @since 2025-11-03
+ */
+ ResultDomain saveTempFile(MultipartFile file, String module, String businessId);
+
+}
diff --git a/urbanLifelineServ/apis/api-log/pom.xml b/urbanLifelineServ/apis/api-log/pom.xml
new file mode 100644
index 0000000..9a82998
--- /dev/null
+++ b/urbanLifelineServ/apis/api-log/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ apis
+ 1.0.0
+
+
+ org.xyzh.apis
+ api-log
+ ${urban-lifeline.version}
+ jar
+
+
+ 21
+ 21
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-log/src/main/java/org/xyzh/api/log/dto/TbSysLogDTO.java b/urbanLifelineServ/apis/api-log/src/main/java/org/xyzh/api/log/dto/TbSysLogDTO.java
new file mode 100644
index 0000000..30f2514
--- /dev/null
+++ b/urbanLifelineServ/apis/api-log/src/main/java/org/xyzh/api/log/dto/TbSysLogDTO.java
@@ -0,0 +1,51 @@
+package org.xyzh.api.log.dto;
+
+import com.alibaba.fastjson2.JSONObject;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.dto.BaseDTO;
+
+/**
+ * @description 系统日志DTO
+ * @filename TbSysLogDTO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-04
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "系统日志DTO")
+public class TbSysLogDTO extends BaseDTO {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "日志ID")
+ private String logId;
+
+ @Schema(description = "日志类型")
+ private Integer type;
+
+ @Schema(description = "日志级别")
+ private String level;
+
+ @Schema(description = "模块")
+ private String module;
+
+ @Schema(description = "IP地址")
+ private String ipAddress;
+
+ @Schema(description = "IP来源")
+ private String ip_source;
+
+ @Schema(description = "浏览器")
+ private String browser;
+
+ @Schema(description = "操作系统")
+ private String os;
+
+ @Schema(description = "日志消息")
+ private String message;
+
+ @Schema(description = "日志数据")
+ private JSONObject data;
+}
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-log/src/main/java/org/xyzh/api/log/dto/TbSysLoginLogDTO.java b/urbanLifelineServ/apis/api-log/src/main/java/org/xyzh/api/log/dto/TbSysLoginLogDTO.java
new file mode 100644
index 0000000..c6d4818
--- /dev/null
+++ b/urbanLifelineServ/apis/api-log/src/main/java/org/xyzh/api/log/dto/TbSysLoginLogDTO.java
@@ -0,0 +1,56 @@
+package org.xyzh.api.log.dto;
+
+import java.util.Date;
+import com.alibaba.fastjson2.annotation.JSONField;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.dto.BaseDTO;
+
+/**
+ * @description 系统登录日志DTO
+ * @filename TbSysLoginLogDTO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-04
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "系统登录日志DTO")
+public class TbSysLoginLogDTO extends BaseDTO {
+
+ @Schema(description = "用户ID")
+ private String userId;
+
+ @Schema(description = "用户名")
+ private String username;
+
+ @Schema(description = "IP地址")
+ private String ipAddress;
+
+ @Schema(description = "IP来源")
+ private String ipSource;
+
+ @Schema(description = "浏览器")
+ private String browser;
+
+ @Schema(description = "操作系统")
+ private String os;
+
+ @Schema(description = "密码")
+ private String password;
+
+ @Schema(description = "登录时间")
+ @JSONField(format = "yyyy-MM-dd HH:mm:ss")
+ private Date loginTime;
+
+ @Schema(description = "状态")
+ private Integer status;
+
+ @Schema(description = "错误次数")
+ private Integer errorCount;
+
+ @Schema(description = "消息")
+ private String message;
+}
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-message/pom.xml b/urbanLifelineServ/apis/api-message/pom.xml
new file mode 100644
index 0000000..aac432d
--- /dev/null
+++ b/urbanLifelineServ/apis/api-message/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ apis
+ 1.0.0
+
+
+ org.xyzh.apis
+ api-message
+ ${urban-lifeline.version}
+ jar
+
+
+ 21
+ 21
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageChannelDTO.java b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageChannelDTO.java
new file mode 100644
index 0000000..73974a4
--- /dev/null
+++ b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageChannelDTO.java
@@ -0,0 +1,43 @@
+package org.xyzh.api.message.dto;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.dto.BaseDTO;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * @description 消息渠道配置DTO
+ * @filename TbMessageChannelDTO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "消息渠道配置DTO")
+public class TbMessageChannelDTO extends BaseDTO {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "渠道ID")
+ private String channelId;
+
+ @Schema(description = "渠道编码:app/sms/email/wechat/dingtalk等")
+ private String channelCode;
+
+ @Schema(description = "渠道名称")
+ private String channelName;
+
+ @Schema(description = "渠道描述")
+ private String channelDesc;
+
+ @Schema(description = "渠道配置(JSON格式)")
+ private String config;
+
+ @Schema(description = "渠道状态:enabled-启用/disabled-禁用/maintenance-维护中")
+ private String status;
+
+ @Schema(description = "优先级(数字越大优先级越高)")
+ private Integer priority;
+}
+
diff --git a/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageDTO.java b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageDTO.java
new file mode 100644
index 0000000..ce2895f
--- /dev/null
+++ b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageDTO.java
@@ -0,0 +1,37 @@
+package org.xyzh.api.message.dto;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.dto.BaseDTO;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * @description 消息DTO
+ * @filename TbMessageDTO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-04
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "消息DTO")
+public class TbMessageDTO extends BaseDTO {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "消息ID")
+ private String messageId;
+
+
+ @Schema(description = "消息标题")
+ private String title;
+
+ @Schema(description = "消息内容")
+ private String content;
+
+ @Schema(description = "消息类型")
+ private String type;
+
+ @Schema(description = "消息状态")
+ private String status;
+}
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageRangeDTO.java b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageRangeDTO.java
new file mode 100644
index 0000000..ee357ac
--- /dev/null
+++ b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageRangeDTO.java
@@ -0,0 +1,33 @@
+package org.xyzh.api.message.dto;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.dto.BaseDTO;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * @description 消息发送范围DTO
+ * @filename TbMessageRangeDTO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "消息发送范围DTO")
+public class TbMessageRangeDTO extends BaseDTO {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "消息ID")
+ private String messageId;
+
+ @Schema(description = "目标类型:user-指定用户/dept-部门/role-角色/all-全员")
+ private String targetType;
+
+ @Schema(description = "目标ID(用户、部门、角色ID等,all类型时为空)")
+ private String targetId;
+
+ @Schema(description = "发送渠道:app/sms/email/wechat/dingtalk等")
+ private String channel;
+}
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageReceiverDTO.java b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageReceiverDTO.java
new file mode 100644
index 0000000..acc3f94
--- /dev/null
+++ b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/dto/TbMessageReceiverDTO.java
@@ -0,0 +1,41 @@
+package org.xyzh.api.message.dto;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.dto.BaseDTO;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.time.ZonedDateTime;
+
+/**
+ * @description 用户消息接收记录DTO
+ * @filename TbMessageReceiverDTO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "用户消息接收记录DTO")
+public class TbMessageReceiverDTO extends BaseDTO {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "消息ID")
+ private String messageId;
+
+ @Schema(description = "用户ID")
+ private String userId;
+
+ @Schema(description = "接收渠道:app/sms/email/wechat/dingtalk等")
+ private String channel;
+
+ @Schema(description = "消息状态:unread-未读/read-已读/handled-已处理/deleted-已删除")
+ private String status;
+
+ @Schema(description = "阅读时间")
+ private ZonedDateTime readTime;
+
+ @Schema(description = "处理时间")
+ private ZonedDateTime handleTime;
+}
diff --git a/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/service/MessageService.java b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/service/MessageService.java
new file mode 100644
index 0000000..180128e
--- /dev/null
+++ b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/service/MessageService.java
@@ -0,0 +1,135 @@
+package org.xyzh.api.message.service;
+
+import org.xyzh.api.message.dto.TbMessageDTO;
+import org.xyzh.api.message.vo.MessageVO;
+import org.xyzh.common.core.domain.ResultDomain;
+import org.xyzh.common.core.page.PageParam;
+
+
+/**
+ * @description 消息服务接口
+ * @filename MessageService.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+public interface MessageService {
+
+ //================= 用户查看消息列表 =================
+
+ /**
+ * @description 获取我的消息列表
+ * @param userId 用户ID
+ * @return ResultDomain 消息列表
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getMyMessageList(TbMessageDTO filter);
+
+ /**
+ * @description 获取我的消息分页列表
+ * @param TbMessageDTO filter 消息过滤条件
+ * @param PageParam pageParam 分页参数
+ * @return ResultDomain 消息分页列表
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getMyMessagePage(TbMessageDTO filter, PageParam pageParam);
+
+ /**
+ * @description 获取我的消息详情
+ * @param messageId 消息ID
+ * @return ResultDomain 消息详情
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getMyMessageDetail(String messageId);
+
+ // ================= 用户处理消息 =================
+ /**
+ * @description 用户处理消息
+ * @param messageId 消息ID
+ * @param status 消息状态
+ * @return ResultDomain 消息处理结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain handleMessage(String messageId, String status);
+
+ // ================= 管理员查看消息列表 =================
+ /**
+ * @description 获取消息列表
+ * @param TbMessageDTO filter 消息过滤条件
+ * @return ResultDomain 消息列表
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getMessageList(TbMessageDTO filter);
+
+ /**
+ * @description 获取消息分页列表
+ * @param TbMessageDTO filter 消息过滤条件
+ * @param PageParam pageParam 分页参数
+ * @return ResultDomain 消息分页列表
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getMessagePage(TbMessageDTO filter, PageParam pageParam);
+
+
+ /**
+ * @description 获取消息详情
+ * @param messageId 消息ID
+ * @return ResultDomain 消息详情
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getMessageDetail(String messageId);
+
+ // ================= 管理员处理消息 =================
+
+ /**
+ * @description 创建消息
+ * @param MessageVO messageVO 消息VO
+ * @return ResultDomain 创建结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain createMessage(MessageVO messageVO);
+
+ /**
+ * @description 更新消息
+ * @param MessageVO messageVO 消息VO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateMessage(MessageVO messageVO);
+
+ /**
+ * @description 删除消息
+ * @param messageId 消息ID
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain deleteMessage(String messageId);
+
+ /**
+ * @description 发送消息
+ * @param MessageVO messageVO 消息VO
+ * @return ResultDomain 发送结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain sendMessage(MessageVO messageVO);
+
+ /**
+ * @description 撤回消息
+ * @param messageId 消息ID
+ * @return ResultDomain 撤回结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain withdrawMessage(String messageId);
+}
diff --git a/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/vo/MessageRangeChannelVO.java b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/vo/MessageRangeChannelVO.java
new file mode 100644
index 0000000..f38780d
--- /dev/null
+++ b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/vo/MessageRangeChannelVO.java
@@ -0,0 +1,48 @@
+package org.xyzh.api.message.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.vo.BaseVO;
+
+/**
+ * @description 消息发送范围和渠道VO(Range和Channel的平铺组合)
+ * @filename MessageRangeChannelVO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "消息发送范围和渠道VO")
+public class MessageRangeChannelVO extends BaseVO {
+
+ // ========== Range相关字段 ==========
+ @Schema(description = "消息ID")
+ private String messageId;
+
+ @Schema(description = "目标类型:user-指定用户/dept-部门/role-角色/all-全员")
+ private String targetType;
+
+ @Schema(description = "目标ID(用户、部门、角色ID等,all类型时为空)")
+ private String targetId;
+
+ @Schema(description = "目标名称(用户名、部门名、角色名等,用于前端展示)")
+ private String targetName;
+
+ // ========== Channel相关字段 ==========
+ @Schema(description = "渠道编码:app/sms/email/wechat/dingtalk等")
+ private String channelCode;
+
+ @Schema(description = "渠道名称")
+ private String channelName;
+
+ @Schema(description = "渠道描述")
+ private String channelDesc;
+
+ @Schema(description = "渠道状态:enabled-启用/disabled-禁用/maintenance-维护中")
+ private String channelStatus;
+
+ @Schema(description = "渠道优先级(数字越大优先级越高)")
+ private Integer channelPriority;
+}
diff --git a/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/vo/MessageVO.java b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/vo/MessageVO.java
new file mode 100644
index 0000000..06decef
--- /dev/null
+++ b/urbanLifelineServ/apis/api-message/src/main/java/org/xyzh/api/message/vo/MessageVO.java
@@ -0,0 +1,44 @@
+package org.xyzh.api.message.vo;
+
+import java.util.List;
+
+import org.xyzh.api.message.dto.TbMessageReceiverDTO;
+import org.xyzh.common.vo.BaseVO;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @description 消息VO(包含消息详情和发送范围)
+ * @filename MessageVO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "消息VO")
+public class MessageVO extends BaseVO {
+
+ @Schema(description = "消息ID")
+ private String messageId;
+
+ @Schema(description = "消息标题")
+ private String title;
+
+ @Schema(description = "消息内容")
+ private String content;
+
+ @Schema(description = "消息类型")
+ private String type;
+
+ @Schema(description = "消息状态")
+ private String status;
+
+ @Schema(description = "消息发送范围列表")
+ private List messageRanges;
+
+ @Schema(description = "消息接收记录列表(管理员查看时使用)")
+ private List messageReceivers;
+}
diff --git a/urbanLifelineServ/apis/api-message/消息系统设计说明.md b/urbanLifelineServ/apis/api-message/消息系统设计说明.md
new file mode 100644
index 0000000..e0d53c5
--- /dev/null
+++ b/urbanLifelineServ/apis/api-message/消息系统设计说明.md
@@ -0,0 +1,238 @@
+# 消息系统设计说明
+
+## 📊 数据表结构
+
+### 1. tb_message - 消息主表
+存储消息的基本信息
+
+**主要字段:**
+- `message_id` - 消息ID(主键)
+- `title` - 消息标题
+- `content` - 消息内容
+- `type` - 消息类型
+- `status` - 消息状态
+
+### 2. tb_message_range - 消息发送范围定义表
+定义消息要发送给哪些对象,通过什么渠道
+
+**主要字段:**
+- `message_id` - 消息ID
+- `target_type` - 目标类型(user/dept/role/all)
+- `target_id` - 目标ID(用户、部门、角色ID等)
+- `channel` - 发送渠道(app/sms/email/wechat等)
+- **唯一约束:** (message_id, target_type, target_id, channel)
+
+**使用示例:**
+- 发送给部门001,通过app:`{target_type: 'dept', target_id: 'D001', channel: 'app'}`
+- 发送给部门001,通过sms:`{target_type: 'dept', target_id: 'D001', channel: 'sms'}`
+- 发送给全员,通过app:`{target_type: 'all', target_id: null, channel: 'app'}`
+
+### 3. tb_message_receiver - 用户消息接收记录表
+记录每个用户实际收到的消息及处理状态
+
+**主要字段:**
+- `message_id` - 消息ID
+- `user_id` - 用户ID
+- `channel` - 接收渠道
+- `status` - 消息状态(unread/read/handled/deleted)
+- `read_time` - 阅读时间
+- `handle_time` - 处理时间
+- **唯一约束:** (message_id, user_id, channel)
+
+**索引:**
+- `idx_message_user_user_status` - 快速查询用户消息列表
+- `idx_message_user_message` - 快速查询消息的接收情况
+
+### 4. tb_message_channel - 消息渠道配置表
+管理各种消息发送渠道的配置
+
+**主要字段:**
+- `channel_id` - 渠道ID(主键)
+- `channel_code` - 渠道编码(app/sms/email/wechat/dingtalk)
+- `channel_name` - 渠道名称
+- `config` - 渠道配置(JSON格式,存储API密钥等)
+- `status` - 渠道状态(enabled/disabled/maintenance)
+- `priority` - 优先级
+
+**预置渠道:**
+- app - 应用内消息(已启用)
+- sms - 短信通知(已禁用)
+- email - 邮件通知(已禁用)
+- wechat - 微信通知(已禁用)
+- dingtalk - 钉钉通知(已禁用)
+
+## 📦 DTO/VO 结构
+
+### TbMessageDTO
+消息基本信息DTO
+
+**字段:**
+- messageId
+- title
+- content
+- type
+- status
+
+### TbMessageRangeDTO
+消息发送范围DTO
+
+**字段:**
+- messageId
+- targetType - 目标类型
+- targetId - 目标ID
+- channel - 发送渠道
+
+### TbMessageReceiverDTO
+用户消息接收记录DTO
+
+**字段:**
+- messageId
+- userId
+- channel
+- status
+- readTime
+- handleTime
+
+### TbMessageChannelDTO
+消息渠道配置DTO
+
+**字段:**
+- channelId
+- channelCode
+- channelName
+- channelDesc
+- config
+- status
+- priority
+
+### MessageVO
+消息视图对象(用于创建和查看消息)
+
+**字段:**
+- messageId
+- title
+- content
+- type
+- status
+- createTime
+- creator
+- messageRanges - 消息发送范围列表
+- messageReceivers - 消息接收记录列表(管理员查看时使用)
+
+## 🔄 业务流程
+
+### 1. 创建并发送消息
+
+```java
+// 创建消息
+MessageVO messageVO = new MessageVO();
+messageVO.setTitle("系统维护通知");
+messageVO.setContent("系统将于今晚22:00进行维护");
+messageVO.setType("notice");
+
+// 定义发送范围(发给IT部门,通过app和email)
+List ranges = new ArrayList<>();
+ranges.add(new TbMessageRangeDTO() {{
+ setTargetType("dept");
+ setTargetId("DEPT_IT");
+ setChannel("app");
+}});
+ranges.add(new TbMessageRangeDTO() {{
+ setTargetType("dept");
+ setTargetId("DEPT_IT");
+ setChannel("email");
+}});
+messageVO.setMessageRanges(ranges);
+
+// 发送消息
+messageService.sendMessage(messageVO);
+```
+
+**系统处理:**
+1. 在 `tb_message` 中创建消息记录
+2. 在 `tb_message_range` 中保存发送范围
+3. 根据 `target_type` 和 `target_id` 查询具体用户列表
+4. 在 `tb_message_receiver` 中为每个用户创建接收记录
+5. 根据 `channel` 调用相应的渠道服务发送消息
+
+### 2. 用户查看消息列表
+
+```sql
+-- 查询用户未读消息
+SELECT m.*, r.status, r.read_time, r.channel
+FROM message.tb_message m
+JOIN message.tb_message_receiver r ON m.message_id = r.message_id
+WHERE r.user_id = 'USER_001'
+ AND r.status = 'unread'
+ AND r.deleted = false
+ORDER BY m.create_time DESC;
+```
+
+### 3. 用户处理消息
+
+```java
+// 标记消息为已读
+messageService.handleMessage(messageId, "read");
+
+// 标记消息为已处理
+messageService.handleMessage(messageId, "handled");
+```
+
+**系统处理:**
+- 更新 `tb_message_receiver` 表的 `status` 字段
+- 根据状态更新 `read_time` 或 `handle_time`
+
+### 4. 管理员查看消息统计
+
+```sql
+-- 查询某条消息的发送统计
+SELECT
+ r.channel,
+ r.status,
+ COUNT(*) as count
+FROM message.tb_message_receiver r
+WHERE r.message_id = 'MSG_001'
+ AND r.deleted = false
+GROUP BY r.channel, r.status;
+```
+
+## 🎯 设计优势
+
+1. **职责分离**
+ - `tb_message_range` - 定义发送规则
+ - `tb_message_receiver` - 记录实际接收情况
+
+2. **多渠道支持**
+ - 同一消息可通过多个渠道发送
+ - 渠道配置独立管理
+ - 易于扩展新渠道
+
+3. **灵活的目标定义**
+ - 支持按用户、部门、角色、全员发送
+ - 同一目标可使用不同渠道
+
+4. **完整的状态跟踪**
+ - 记录阅读时间、处理时间
+ - 支持已读/未读/已处理/已删除等状态
+
+5. **性能优化**
+ - 合理的索引设计
+ - 支持高效的用户消息查询
+
+## 📝 注意事项
+
+1. **数据一致性**
+ - 发送消息时,确保 `tb_message_range` 和 `tb_message_receiver` 的事务一致性
+
+2. **渠道验证**
+ - 发送前检查 `tb_message_channel` 中渠道是否启用
+ - 根据 `priority` 选择备用渠道
+
+3. **性能考虑**
+ - 全员消息(target_type='all')需要异步处理
+ - 大量用户时分批创建 `tb_message_receiver` 记录
+
+4. **软删除**
+ - 所有表都使用软删除(deleted字段)
+ - 查询时注意添加 `WHERE deleted = false` 条件
+
diff --git a/urbanLifelineServ/apis/api-system/pom.xml b/urbanLifelineServ/apis/api-system/pom.xml
new file mode 100644
index 0000000..1e68a18
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ apis
+ 1.0.0
+
+
+ org.xyzh.apis
+ api-system
+ ${urban-lifeline.version}
+ jar
+
+
+ 21
+ 21
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/DeptRoleService.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/DeptRoleService.java
new file mode 100644
index 0000000..7d4a5ff
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/DeptRoleService.java
@@ -0,0 +1,230 @@
+package org.xyzh.api.system.service;
+
+import org.xyzh.api.system.vo.PermissionVO;
+import org.xyzh.api.system.vo.UserDeptRoleVO;
+import org.xyzh.common.core.domain.ResultDomain;
+import org.xyzh.common.core.page.PageRequest;
+import org.xyzh.common.dto.sys.TbSysDeptDTO;
+import org.xyzh.common.dto.sys.TbSysDeptRoleDTO;
+import org.xyzh.common.dto.sys.TbSysRoleDTO;
+
+/**
+ * @description 部门角色服务接口
+ * @filename DeptRoleService.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+public interface DeptRoleService {
+
+ // ================= 部门管理 =================
+ /**
+ * @description 插入部门
+ * @param deptDTO 部门DTO
+ * @return ResultDomain 插入结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain insertDept(TbSysDeptDTO deptDTO);
+
+ /**
+ * @description 更新部门
+ * @param deptDTO 部门DTO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateDept(TbSysDeptDTO deptDTO);
+
+ /**
+ * @description 根据ID删除部门
+ * @param deptDTO 部门DTO
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain deleteDept(TbSysDeptDTO deptDTO);
+
+ /**
+ * @description 根据ID查询部门
+ * @param filter 部门VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getDept(UserDeptRoleVO filter);
+
+ /**
+ * @description 根据条件查询部门列表
+ * @param filter 部门VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getDeptList(UserDeptRoleVO filter);
+
+ /**
+ * @description 根据条件查询部门分页列表
+ * @param pageRequest 部门VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getDeptPage(PageRequest pageRequest);
+
+ /**
+ * @description 获取部门树
+ * @param filter 部门VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getDeptTree(UserDeptRoleVO filter);
+
+ // ================= 角色管理 =================
+ /**
+ * @description 插入角色
+ * @param roleDTO 角色DTO
+ * @return ResultDomain 插入结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain insertRole(TbSysRoleDTO roleDTO);
+
+ /**
+ * @description 更新角色
+ * @param roleDTO 角色DTO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateRole(TbSysRoleDTO roleDTO);
+
+ /**
+ * @description 根据ID删除角色
+ * @param roleDTO 角色DTO
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain deleteRole(TbSysRoleDTO roleDTO);
+
+ /**
+ * @description 根据ID查询角色
+ * @param filter 角色VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getRole(UserDeptRoleVO filter);
+
+ /**
+ * @description 根据条件查询角色列表
+ * @param filter 角色VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getRoleList(UserDeptRoleVO filter);
+
+ /**
+ * @description 根据条件查询角色分页列表
+ * @param pageRequest 角色VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getRolePage(PageRequest pageRequest);
+
+ /**
+ * @description 根据部门ID获取角色列表
+ * @param deptId 部门ID
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getRoleListByDeptId(String deptId);
+
+ /**
+ * @description 根据用户ID获取角色列表
+ * @param userId 用户ID
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getRoleListByUserId(String userId);
+
+ // ================= 部门角色关联管理 =================
+ /**
+ * @description 插入部门角色关联
+ * @param deptRoleDTO 部门角色DTO
+ * @return ResultDomain 插入结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain insertDeptRole(TbSysDeptRoleDTO deptRoleDTO);
+
+ /**
+ * @description 更新部门角色关联
+ * @param deptRoleDTO 部门角色DTO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateDeptRole(TbSysDeptRoleDTO deptRoleDTO);
+
+ /**
+ * @description 根据ID删除部门角色关联
+ * @param deptRoleDTO 部门角色DTO
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain deleteDeptRole(TbSysDeptRoleDTO deptRoleDTO);
+
+ /**
+ * @description 根据ID查询部门角色关联
+ * @param filter 部门角色VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getDeptRole(UserDeptRoleVO filter);
+
+ /**
+ * @description 根据条件查询部门角色关联列表
+ * @param filter 部门角色VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getDeptRoleList(UserDeptRoleVO filter);
+
+ /**
+ * @description 根据条件查询部门角色关联分页列表
+ * @param pageRequest 部门角色VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getDeptRolePage(PageRequest pageRequest);
+
+ // ==================== 角色权限关联 ================================
+ /**
+ * @description 设置角色的权限
+ * @param permissionVO 权限VO roleId对应多个permissionId
+ * @return 返回值描述
+ * @author yslg
+ * @since 2025-11-10
+ */
+ ResultDomain setRolePermission(PermissionVO permissionVO);
+
+ /**
+ * @description 获取角色的权限列表
+ * @param permissionVO 权限VO
+ * @return 返回值描述
+ * @author yslg
+ * @since 2025-11-10
+ */
+ ResultDomain getRolePermissionList(PermissionVO permissionVO);
+}
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/ModulePermissionService.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/ModulePermissionService.java
new file mode 100644
index 0000000..d3181b5
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/ModulePermissionService.java
@@ -0,0 +1,148 @@
+package org.xyzh.api.system.service;
+
+import org.xyzh.api.system.vo.PermissionVO;
+import org.xyzh.common.core.domain.ResultDomain;
+import org.xyzh.common.dto.sys.TbSysModuleDTO;
+import org.xyzh.common.dto.sys.TbSysPermissionDTO;
+import org.xyzh.common.dto.sys.TbSysRolePermissionDTO;
+import org.xyzh.common.core.page.PageRequest;
+
+/**
+ * @description 模块权限服务接口
+ * @filename ModulePermissionService.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+public interface ModulePermissionService {
+
+ // ================= 模块管理 =================
+ /**
+ * @description 插入模块
+ * @param moduleDTO 模块DTO
+ * @return ResultDomain 插入结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain insertModule(TbSysModuleDTO moduleDTO);
+
+ /**
+ * @description 更新模块
+ * @param moduleDTO 模块DTO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateModule(TbSysModuleDTO moduleDTO);
+
+ /**
+ * @description 根据ID删除模块
+ * @param moduleDTO 模块DTO
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain deleteModule(TbSysModuleDTO moduleDTO);
+
+ /**
+ * @description 获取模块分页数据
+ * @param
+ * @return 返回值描述
+ * @author yslg
+ * @since 2025-11-10
+ */
+ ResultDomain getModulePage(PageRequest pageRequest);
+
+ /**
+ * @description 查询模块列表
+ * @param filter 模块VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getModuleList(PermissionVO filter);
+
+ // ================= 权限管理 =================
+ /**
+ * @description 插入权限
+ * @param permissionDTO 权限DTO
+ * @return ResultDomain 插入结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain insertPermission(TbSysPermissionDTO permissionDTO);
+
+ /**
+ * @description 更新权限
+ * @param permissionDTO 权限DTO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updatePermission(TbSysPermissionDTO permissionDTO);
+
+ /**
+ * @description 根据ID删除权限
+ * @param permissionDTO 权限DTO
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain deletePermission(TbSysPermissionDTO permissionDTO);
+
+ /**
+ * @description 根据模块ID获取权限列表
+ * @param moduleId 模块ID
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getPermissionListByModuleId(String moduleId);
+
+
+ // ================= 模块权限查询 =================
+ /**
+ * @description 根据条件查询模块权限
+ * @param filter 模块权限VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getModulePermission(PermissionVO filter);
+
+ /**
+ * @description 根据条件查询模块权限列表
+ * @param filter 模块权限VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getModulePermissionList(PermissionVO filter);
+
+ /**
+ * @description 根据条件查询模块权限分页列表
+ * @param pageRequest 模块权限VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getModulePermissionPage(PageRequest pageRequest);
+
+ /**
+ * @description 根据角色ID获取模块权限列表
+ * @param roleId 角色ID
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getModulePermissionListByRoleId(String roleId);
+
+ /**
+ * @description 根据用户ID获取用户的所有权限
+ * @param userId 用户ID
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getUserPermissions(String userId);
+}
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/SysConfigService.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/SysConfigService.java
new file mode 100644
index 0000000..8f12f8a
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/SysConfigService.java
@@ -0,0 +1,88 @@
+package org.xyzh.api.system.service;
+
+import org.xyzh.api.system.vo.SysConfigVO;
+import org.xyzh.common.core.domain.ResultDomain;
+import org.xyzh.common.core.page.PageRequest;
+import org.xyzh.common.dto.sys.TbSysConfigDTO;
+
+/**
+ * @description 系统配置服务接口
+ * @filename SysConfigService.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+public interface SysConfigService {
+
+ /**
+ * @description 插入系统配置
+ * @param configDTO 系统配置DTO
+ * @return ResultDomain 插入结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain insertConfig(TbSysConfigDTO configDTO);
+
+ /**
+ * @description 更新系统配置
+ * @param configDTO 系统配置DTO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateConfig(TbSysConfigDTO configDTO);
+
+ /**
+ * @description 根据ID删除系统配置
+ * @param configDTO 系统配置DTO
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain deleteConfig(TbSysConfigDTO configDTO);
+
+ /**
+ * @description 根据ID查询系统配置
+ * @param filter 系统配置VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getConfig(SysConfigVO filter);
+
+ /**
+ * @description 根据条件查询系统配置列表
+ * @param filter 系统配置VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getConfigList(SysConfigVO filter);
+
+ /**
+ * @description 根据条件查询系统配置分页列表
+ * @param filter 系统配置VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getConfigPage(PageRequest filter);
+
+ /**
+ * @description 根据配置键获取配置值
+ * @param key 配置键
+ * @return ResultDomain 配置值
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getConfigValueByKey(String key);
+
+ /**
+ * @description 根据模块ID获取配置列表
+ * @param moduleId 模块ID
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getConfigListByModuleId(String moduleId);
+}
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/SysUserService.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/SysUserService.java
new file mode 100644
index 0000000..7b8df7f
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/SysUserService.java
@@ -0,0 +1,180 @@
+package org.xyzh.api.system.service;
+
+import org.xyzh.api.system.vo.SysUserVO;
+import org.xyzh.api.system.vo.UserDeptRoleVO;
+import org.xyzh.common.core.domain.ResultDomain;
+import org.xyzh.common.core.page.PageRequest;
+import org.xyzh.common.dto.sys.TbSysUserDTO;
+import org.xyzh.common.dto.sys.TbSysUserInfoDTO;
+import org.xyzh.common.dto.sys.TbSysUserRoleDTO;
+
+/**
+ * @description 用户服务接口
+ * @filename SysUserService.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+public interface SysUserService {
+
+ // ================= 用户基本信息管理 =================
+ /**
+ * @description 插入用户
+ * @param userVO 用户VO
+ * @return ResultDomain 插入结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain insertUser(SysUserVO userVO);
+
+ /**
+ * @description 更新用户
+ * @param userVO 用户VO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateUser(SysUserVO userVO);
+
+ /**
+ * @description 根据ID删除用户
+ * @param TbSysUserDTO userDTO 用户DTO
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain deleteUser(TbSysUserDTO userDTO);
+
+ /**
+ * @description 根据ID查询用户
+ * @param filter 用户VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getUser(SysUserVO filter);
+
+ /**
+ * @description 用户登录查询
+ * @param filter 用户VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getLoginUser(SysUserVO filter);
+
+ /**
+ * @description 根据条件查询用户列表
+ * @param filter 用户VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getUserList(SysUserVO filter);
+
+ /**
+ * @description 根据条件查询用户分页列表
+ * @param filter 用户VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getUserPage(PageRequest pageRequest);
+
+ /**
+ * @description 根据用户名查询用户
+ * @param username 用户名
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getUserByUsername(String username);
+
+ /**
+ * @description 更新用户密码
+ * @param userId 用户ID
+ * @param oldPassword 旧密码
+ * @param newPassword 新密码
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateUserPassword(String userId, String oldPassword, String newPassword);
+
+ /**
+ * @description 重置用户密码
+ * @param userId 用户ID
+ * @return ResultDomain 新密码
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain resetUserPassword(String userId);
+
+ /**
+ * @description 更新用户状态
+ * @param userId 用户ID
+ * @param status 状态
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateUserStatus(String userId, String status);
+
+ // ================= 用户详细信息管理 =================
+ /**
+ * @description 更新用户详细信息
+ * @param userInfoDTO 用户详细信息DTO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateUserInfo(TbSysUserInfoDTO userInfoDTO);
+
+ /**
+ * @description 根据用户ID获取用户详细信息
+ * @param userId 用户ID
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getUserInfo(String userId);
+
+ // ================= 用户角色关联管理 =================
+ /**
+ * @description 添加用户角色关联
+ * @param userRoleDTO 用户角色DTO
+ * @return ResultDomain 添加结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain addUserRole(TbSysUserRoleDTO userRoleDTO);
+
+ /**
+ * @description 删除用户角色关联
+ * @param userRoleDTO 用户角色DTO
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain removeUserRole(TbSysUserRoleDTO userRoleDTO);
+
+ /**
+ * @description 批量设置用户角色
+ * @param userId 用户ID
+ * @param[] roleIds 角色ID数组
+ * @return ResultDomain 设置结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain setUserRoles(String userId, String[] roleIds);
+
+ // ================= 用户完整信息查询 =================
+ /**
+ * @description 获取用户完整信息(包含部门和角色)
+ * @param userId 用户ID
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getUserWithDeptRole(String userId);
+}
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/ViewService.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/ViewService.java
new file mode 100644
index 0000000..aae0176
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/ViewService.java
@@ -0,0 +1,99 @@
+package org.xyzh.api.system.service;
+
+import org.xyzh.api.system.vo.PermissionVO;
+import org.xyzh.common.core.domain.ResultDomain;
+import org.xyzh.common.dto.sys.TbSysViewDTO;
+import org.xyzh.common.dto.sys.TbSysViewPermissionDTO;
+import org.xyzh.common.core.page.PageRequest;
+/**
+ * @description 视图服务接口
+ * @filename ViewService.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+public interface ViewService {
+
+ // ================= 视图管理 =================
+ /**
+ * @description 插入视图
+ * @param viewDTO 视图DTO
+ * @return ResultDomain 插入结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain insertView(TbSysViewDTO viewDTO);
+
+ /**
+ * @description 更新视图
+ * @param viewDTO 视图DTO
+ * @return ResultDomain 更新结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain updateView(TbSysViewDTO viewDTO);
+
+ /**
+ * @description 根据ID删除视图
+ * @param viewDTO 视图DTO
+ * @return ResultDomain 删除结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain deleteView(TbSysViewDTO viewDTO);
+
+ /**
+ * @description 根据ID查询视图
+ * @param filter 视图VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getView(PermissionVO filter);
+
+ /**
+ * @description 根据条件查询视图列表
+ * @param filter 视图VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getViewList(PermissionVO filter);
+
+ /**
+ * @description 根据条件查询视图分页列表
+ * @param filter 视图VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getViewPage(PageRequest filter);
+
+ /**
+ * @description 获取视图树(包含子视图)
+ * @param filter 视图VO
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getViewTree(PermissionVO filter);
+
+ // ================= 视图权限关联管理 =================
+ /**
+ * @description 设置视图权限
+ * @param permissionVO 视图ID 和权限ID数组
+ * @return ResultDomain 设置结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain setViewPermissions(PermissionVO permissionVO);
+
+ /**
+ * @description 获取视图的权限列表
+ * @param permissionVO 视图
+ * @return ResultDomain 查询结果
+ * @author yslg
+ * @since 2025-11-05
+ */
+ ResultDomain getViewPermissionList(PermissionVO permissionVO);
+}
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/AclVO.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/AclVO.java
new file mode 100644
index 0000000..281b8d8
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/AclVO.java
@@ -0,0 +1,71 @@
+package org.xyzh.api.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.vo.BaseVO;
+
+/**
+ * @description 访问控制列表视图对象
+ * @filename AclVO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-04
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "访问控制列表视图对象")
+public class AclVO extends BaseVO {
+ // TbSysAclDTO对应字段
+ @Schema(description = "权限ID")
+ private String aclId;
+
+ @Schema(description = "对象类型:article/file/course/...")
+ private String objectType;
+
+ @Schema(description = "对象ID")
+ private String objectId;
+
+ @Schema(description = "主体类型:user/dept/role")
+ private String principalType;
+
+ @Schema(description = "主体ID")
+ private String principalId;
+
+ @Schema(description = "当主体为role且限定到某部门时的部门ID(支持某部门的某角色)")
+ private String principalDeptId;
+
+ @Schema(description = "权限位:1读 2写 4执行")
+ private Integer permission;
+
+ @Schema(description = "允许或显式拒绝", defaultValue = "true")
+ private Boolean allow = true;
+
+ @Schema(description = "是否包含子级(对dept/role生效)", defaultValue = "false")
+ private Boolean includeDescendants = false;
+
+ // TbSysAclPolicyDTO对应字段
+ @Schema(description = "策略ID")
+ private String policyId;
+
+ @Schema(description = "策略名称")
+ private String policyName;
+
+ @Schema(description = "对象类型:article/file/course/..")
+ private String policyObjectType;
+
+ @Schema(description = "编辑层级规则:parent_only/parent_or_same_admin/owner_only/none")
+ private String editHierarchyRule;
+
+ @Schema(description = "可见层级规则 children_all/children_specified/none")
+ private String viewHierarchyRule;
+
+ @Schema(description = "默认权限(无显式ACL时应用)", defaultValue = "0")
+ private Integer defaultPermission = 0;
+
+ @Schema(description = "默认是否允许", defaultValue = "true")
+ private boolean defaultAllow = true;
+
+ @Schema(description = "是否默认应用到子级", defaultValue = "true")
+ private boolean applyToChildren = true;
+}
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/PermissionVO.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/PermissionVO.java
new file mode 100644
index 0000000..37e7602
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/PermissionVO.java
@@ -0,0 +1,221 @@
+package org.xyzh.api.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.vo.BaseVO;
+
+import java.util.List;
+import org.xyzh.common.dto.sys.TbSysModuleDTO;
+import org.xyzh.common.dto.sys.TbSysPermissionDTO;
+import org.xyzh.common.dto.sys.TbSysViewDTO;
+
+/**
+ * @description 权限视图对象
+ * @filename PermissionVO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-04
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "权限视图对象")
+public class PermissionVO extends BaseVO {
+ // TbSysDeptDTO对应字段
+ @Schema(description = "部门ID")
+ private String deptId;
+
+ @Schema(description = "部门名称")
+ private String deptName;
+
+ @Schema(description = "父级部门ID")
+ private String deptParentId;
+
+ @Schema(description = "部门描述")
+ private String deptDescription;
+
+ // TbSysRoleDTO对应字段
+ @Schema(description = "角色ID")
+ private String roleId;
+
+ @Schema(description = "角色名称")
+ private String roleName;
+
+ @Schema(description = "角色描述")
+ private String roleDescription;
+
+ @Schema(description = "角色作用域")
+ private String roleScope;
+
+ @Schema(description = "所属部门ID")
+ private String roleOwnerDeptId;
+
+ @Schema(description = "角色状态")
+ private boolean roleStatus;
+
+ // TbSysModuleDTO对应字段
+ @Schema(description = "模块ID")
+ private String moduleId;
+
+ @Schema(description = "模块名称")
+ private String moduleName;
+
+ @Schema(description = "模块描述")
+ private String moduleDescription;
+
+ // TbSysPermissionDTO对应字段
+ @Schema(description = "权限ID")
+ private String permissionId;
+
+ @Schema(description = "权限名称")
+ private String permissionName;
+
+ @Schema(description = "权限代码")
+ private String permissionCode;
+
+ @Schema(description = "权限描述")
+ private String permissionDescription;
+
+ @Schema(description = "权限状态")
+ private String permissionStatus;
+
+ // TbSysViewDTO对应字段
+ @Schema(description = "视图ID")
+ private String viewId;
+
+ @Schema(description = "视图名称")
+ private String viewName;
+
+ @Schema(description = "父视图ID")
+ private String viewParentId;
+
+ @Schema(description = "URL")
+ private String viewUrl;
+
+ @Schema(description = "组件")
+ private String viewComponent;
+
+ @Schema(description = "图标")
+ private String viewIcon;
+
+ @Schema(description = "类型")
+ private Integer viewType;
+
+ @Schema(description = "布局")
+ private String viewLayout;
+
+ @Schema(description = "排序")
+ private Integer viewOrderNum;
+
+ @Schema(description = "视图描述")
+ private String viewDescription;
+
+ // 角色权限数组
+ @Schema(description = "用户视图权限列表")
+ private List permissionIdList;
+
+ public static TbSysModuleDTO toModuleDTO(PermissionVO vo) {
+ if (vo == null) {
+ return null;
+ }
+ TbSysModuleDTO dto = new TbSysModuleDTO();
+ dto.setModuleId(vo.getModuleId());
+ dto.setName(vo.getModuleName());
+ dto.setDescription(vo.getModuleDescription());
+ dto.setOptsn(vo.getOptsn());
+ dto.setCreator(vo.getCreator());
+ dto.setUpdater(vo.getUpdater());
+ dto.setDeptPath(vo.getDeptPath());
+ dto.setRemark(vo.getRemark());
+ dto.setCreateTime(vo.getCreateTime());
+ dto.setUpdateTime(vo.getUpdateTime());
+ dto.setDeleteTime(vo.getDeleteTime());
+ dto.setDeleted(vo.getDeleted());
+ return dto;
+ }
+
+ public static TbSysPermissionDTO toPermissionDTO(PermissionVO vo) {
+ if (vo == null) {
+ return null;
+ }
+ TbSysPermissionDTO dto = new TbSysPermissionDTO();
+ dto.setPermissionId(vo.getPermissionId());
+ dto.setModuleId(vo.getModuleId());
+ dto.setCode(vo.getPermissionCode());
+ dto.setName(vo.getPermissionName());
+ dto.setDescription(vo.getPermissionDescription());
+ dto.setOptsn(vo.getOptsn());
+ dto.setCreator(vo.getCreator());
+ dto.setUpdater(vo.getUpdater());
+ dto.setDeptPath(vo.getDeptPath());
+ dto.setRemark(vo.getRemark());
+ dto.setCreateTime(vo.getCreateTime());
+ dto.setUpdateTime(vo.getUpdateTime());
+ dto.setDeleteTime(vo.getDeleteTime());
+ dto.setDeleted(vo.getDeleted());
+ return dto;
+ }
+
+ public static TbSysViewDTO toViewDTO(PermissionVO vo) {
+ if (vo == null) {
+ return null;
+ }
+ TbSysViewDTO dto = new TbSysViewDTO();
+ dto.setViewId(vo.getViewId());
+ dto.setName(vo.getViewName());
+ dto.setParentId(vo.getViewParentId());
+ dto.setUrl(vo.getViewUrl());
+ dto.setComponent(vo.getViewComponent());
+ dto.setIcon(vo.getViewIcon());
+ dto.setType(vo.getViewType());
+ dto.setLayout(vo.getViewLayout());
+ dto.setOrderNum(vo.getViewOrderNum());
+ dto.setDescription(vo.getViewDescription());
+ dto.setOptsn(vo.getOptsn());
+ dto.setCreator(vo.getCreator());
+ dto.setUpdater(vo.getUpdater());
+ dto.setDeptPath(vo.getDeptPath());
+ dto.setRemark(vo.getRemark());
+ dto.setCreateTime(vo.getCreateTime());
+ dto.setUpdateTime(vo.getUpdateTime());
+ dto.setDeleteTime(vo.getDeleteTime());
+ dto.setDeleted(vo.getDeleted());
+ return dto;
+ }
+
+ public static PermissionVO fromViewDTO(TbSysViewDTO dto) {
+ if (dto == null) {
+ return null;
+ }
+ PermissionVO vo = new PermissionVO();
+ vo.setViewId(dto.getViewId());
+ vo.setViewName(dto.getName());
+ vo.setViewParentId(dto.getParentId());
+ vo.setViewUrl(dto.getUrl());
+ vo.setViewComponent(dto.getComponent());
+ vo.setViewIcon(dto.getIcon());
+ vo.setViewType(dto.getType());
+ vo.setViewLayout(dto.getLayout());
+ vo.setViewOrderNum(dto.getOrderNum());
+ vo.setViewDescription(dto.getDescription());
+ vo.setOptsn(dto.getOptsn());
+ vo.setCreator(dto.getCreator());
+ vo.setUpdater(dto.getUpdater());
+ vo.setDeptPath(dto.getDeptPath());
+ vo.setRemark(dto.getRemark());
+ vo.setCreateTime(dto.getCreateTime());
+ vo.setUpdateTime(dto.getUpdateTime());
+ vo.setDeleteTime(dto.getDeleteTime());
+ vo.setDeleted(dto.getDeleted());
+ return vo;
+ }
+
+ public static java.util.List fromViewDTOList(java.util.List dtoList) {
+ if (dtoList == null || dtoList.isEmpty()) {
+ return java.util.Collections.emptyList();
+ }
+ return dtoList.stream()
+ .map(PermissionVO::fromViewDTO)
+ .toList();
+ }
+}
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/SysConfigVO.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/SysConfigVO.java
new file mode 100644
index 0000000..ed9fb8b
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/SysConfigVO.java
@@ -0,0 +1,100 @@
+package org.xyzh.api.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.vo.BaseVO;
+import org.xyzh.common.dto.sys.TbSysConfigDTO;
+import com.alibaba.fastjson2.JSONObject;
+
+/**
+ * @description 系统配置视图对象
+ * @filename SysConfigVO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "系统配置视图对象")
+public class SysConfigVO extends BaseVO {
+
+
+ @Schema(description = "配置ID")
+ private String configId;
+
+ @Schema(description = "配置键")
+ private String key;
+
+ @Schema(description = "配置名称")
+ private String name;
+
+ @Schema(description = "配置值")
+ private String value;
+
+ @Schema(description = "数据类型(String, Integer, Boolean, Float, Double)")
+ private String configType;
+
+ @Schema(description = "配置渲染类型(select, input, textarea, checkbox, radio, switch)")
+ private String renderType;
+
+ @Schema(description = "配置描述")
+ private String description;
+
+ @Schema(description = "正则表达式校验规则(JSON)")
+ private JSONObject re;
+
+ @Schema(description = "可选项(JSON),render_type为select、checkbox、radio时使用")
+ private JSONObject options;
+
+ @Schema(description = "配置组")
+ private String group;
+
+ @Schema(description = "模块ID")
+ private String moduleId;
+
+ @Schema(description = "模块名称")
+ private String moduleName;
+
+ @Schema(description = "模块描述")
+ private String moduleDescription;
+
+ @Schema(description = "配置顺序")
+ private Integer orderNum;
+
+ @Schema(description = "状态")
+ private Integer status;
+
+ public static TbSysConfigDTO toDTO(SysConfigVO vo) {
+ if (vo == null) {
+ return null;
+ }
+ TbSysConfigDTO dto = new TbSysConfigDTO();
+ dto.setConfigId(vo.getConfigId());
+ dto.setKey(vo.getKey());
+ dto.setName(vo.getName());
+ dto.setValue(vo.getValue());
+ dto.setConfigType(vo.getConfigType());
+ dto.setRenderType(vo.getRenderType());
+ dto.setDescription(vo.getDescription());
+ dto.setRe(vo.getRe());
+ dto.setOptions(vo.getOptions());
+ dto.setGroup(vo.getGroup());
+ dto.setModuleId(vo.getModuleId());
+ dto.setOrderNum(vo.getOrderNum());
+ dto.setStatus(vo.getStatus());
+ // 基础字段
+ dto.setOptsn(vo.getOptsn());
+ dto.setCreator(vo.getCreator());
+ dto.setUpdater(vo.getUpdater());
+ dto.setDeptPath(vo.getDeptPath());
+ dto.setRemark(vo.getRemark());
+ dto.setCreateTime(vo.getCreateTime());
+ dto.setUpdateTime(vo.getUpdateTime());
+ dto.setDeleteTime(vo.getDeleteTime());
+ dto.setDeleted(vo.getDeleted());
+ return dto;
+ }
+
+}
+
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/SysUserVO.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/SysUserVO.java
new file mode 100644
index 0000000..2e65361
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/SysUserVO.java
@@ -0,0 +1,113 @@
+package org.xyzh.api.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.vo.BaseVO;
+
+import org.xyzh.common.dto.sys.TbSysUserDTO;
+
+import java.util.List;
+
+/**
+ * @description 系统用户视图对象
+ * @filename SysUserVO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "系统用户视图对象")
+public class SysUserVO extends BaseVO {
+
+ // TbSysUserDTO对应字段
+ @Schema(description = "用户ID")
+ private String userId;
+
+ @Schema(description = "用户名")
+ private String username;
+
+ @Schema(description = "密码(敏感信息,仅用于创建/修改)")
+ private String password;
+
+ @Schema(description = "邮箱")
+ private String email;
+
+ @Schema(description = "手机")
+ private String phone;
+
+ @Schema(description = "微信ID")
+ private String wechatId;
+
+ @Schema(description = "用户状态")
+ private String status;
+
+ @Schema(description = "用户类型")
+ private String userType;
+
+ // TbSysUserInfoDTO对应字段
+ @Schema(description = "头像")
+ private String avatar;
+
+ @Schema(description = "性别")
+ private Integer gender;
+
+ @Schema(description = "姓")
+ private String familyName;
+
+ @Schema(description = "名")
+ private String givenName;
+
+ @Schema(description = "全名")
+ private String fullName;
+
+ @Schema(description = "等级")
+ private Integer level;
+
+ @Schema(description = "身份证号")
+ private String idCard;
+
+ @Schema(description = "地址")
+ private String address;
+
+ // 关联信息
+ @Schema(description = "用户部门角色列表")
+ private List deptRoles;
+
+ @Schema(description = "用户角色权限列表")
+ private List rolePermissions;
+
+ @Schema(description = "用户视图权限列表")
+ private List viewPermissions;
+
+ public static TbSysUserDTO toDTO(SysUserVO vo) {
+ if (vo == null) {
+ return null;
+ }
+ TbSysUserDTO dto = new TbSysUserDTO();
+ dto.setUserId(vo.getUserId());
+ dto.setUsername(vo.getUsername());
+ dto.setPassword(vo.getPassword());
+ dto.setEmail(vo.getEmail());
+ dto.setPhone(vo.getPhone());
+ dto.setWechatId(vo.getWechatId());
+ dto.setStatus(vo.getStatus());
+ dto.setUserType(vo.getUserType());
+ dto.setOptsn(vo.getOptsn());
+ dto.setCreator(vo.getCreator());
+ dto.setUpdater(vo.getUpdater());
+ dto.setDeptPath(vo.getDeptPath());
+ dto.setRemark(vo.getRemark());
+ dto.setCreateTime(vo.getCreateTime());
+ dto.setUpdateTime(vo.getUpdateTime());
+ dto.setDeleteTime(vo.getDeleteTime());
+ dto.setDeleted(vo.getDeleted());
+ return dto;
+ }
+
+ public static TbSysUserDTO toFilter(SysUserVO vo) {
+ return toDTO(vo);
+ }
+}
+
diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/UserDeptRoleVO.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/UserDeptRoleVO.java
new file mode 100644
index 0000000..b29f5a1
--- /dev/null
+++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/vo/UserDeptRoleVO.java
@@ -0,0 +1,195 @@
+package org.xyzh.api.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.xyzh.common.vo.BaseVO;
+import org.xyzh.common.dto.sys.TbSysDeptDTO;
+import org.xyzh.common.dto.sys.TbSysRoleDTO;
+import org.xyzh.common.dto.sys.TbSysDeptRoleDTO;
+
+/**
+ * @description 用户部门角色视图对象
+ * @filename UserDeptRoleVO.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-04
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "用户部门角色视图对象")
+public class UserDeptRoleVO extends BaseVO {
+
+ // TbSysUserDTO对应字段
+ @Schema(description = "用户ID")
+ private String userId;
+
+ @Schema(description = "用户名")
+ private String username;
+
+ @Schema(description = "密码")
+ private String password;
+
+ @Schema(description = "邮箱")
+ private String email;
+
+ @Schema(description = "手机")
+ private String phone;
+
+ @Schema(description = "微信ID")
+ private String wechatId;
+
+ @Schema(description = "用户状态")
+ private String status;
+
+ @Schema(description = "用户类型")
+ private String userType;
+
+ // TbSysUserInfoDTO对应字段
+ @Schema(description = "头像")
+ private String avatar;
+
+ @Schema(description = "性别")
+ private Integer gender;
+
+ @Schema(description = "姓")
+ private String familyName;
+
+ @Schema(description = "名")
+ private String givenName;
+
+ @Schema(description = "全名")
+ private String fullName;
+
+ @Schema(description = "等级")
+ private Integer level;
+
+ @Schema(description = "身份证号")
+ private String idCard;
+
+ @Schema(description = "地址")
+ private String address;
+
+ // TbSysDeptDTO对应字段
+ @Schema(description = "部门ID")
+ private String deptId;
+
+ @Schema(description = "部门名称")
+ private String deptName;
+
+ @Schema(description = "父级部门ID")
+ private String parentId;
+
+ @Schema(description = "部门描述")
+ private String deptDescription;
+
+ // TbSysRoleDTO对应字段(SysRoleVO的字段)
+ @Schema(description = "角色ID")
+ private String roleId;
+
+ @Schema(description = "角色名称")
+ private String roleName;
+
+ @Schema(description = "角色描述")
+ private String roleDescription;
+
+ @Schema(description = "角色作用域")
+ private String scope;
+
+ @Schema(description = "所属部门ID")
+ private String ownerDeptId;
+
+ @Schema(description = "角色状态")
+ private boolean roleStatus;
+
+
+ public static TbSysDeptDTO toDeptDTO(UserDeptRoleVO vo) {
+ if (vo == null) {
+ return null;
+ }
+ TbSysDeptDTO dto = new TbSysDeptDTO();
+ dto.setDeptId(vo.getDeptId());
+ dto.setName(vo.getDeptName());
+ dto.setParentId(vo.getParentId());
+ dto.setDescription(vo.getDeptDescription());
+ dto.setOptsn(vo.getOptsn());
+ dto.setCreator(vo.getCreator());
+ dto.setUpdater(vo.getUpdater());
+ dto.setDeptPath(vo.getDeptPath());
+ dto.setRemark(vo.getRemark());
+ dto.setCreateTime(vo.getCreateTime());
+ dto.setUpdateTime(vo.getUpdateTime());
+ dto.setDeleteTime(vo.getDeleteTime());
+ dto.setDeleted(vo.getDeleted());
+ return dto;
+ }
+
+ public static TbSysRoleDTO toRoleDTO(UserDeptRoleVO vo) {
+ if (vo == null) {
+ return null;
+ }
+ TbSysRoleDTO dto = new TbSysRoleDTO();
+ dto.setRoleId(vo.getRoleId());
+ dto.setName(vo.getRoleName());
+ dto.setDescription(vo.getRoleDescription());
+ dto.setScope(vo.getScope());
+ dto.setOwnerDeptId(vo.getOwnerDeptId());
+ dto.setStatus(vo.isRoleStatus());
+ dto.setOptsn(vo.getOptsn());
+ dto.setCreator(vo.getCreator());
+ dto.setUpdater(vo.getUpdater());
+ dto.setDeptPath(vo.getDeptPath());
+ dto.setRemark(vo.getRemark());
+ dto.setCreateTime(vo.getCreateTime());
+ dto.setUpdateTime(vo.getUpdateTime());
+ dto.setDeleteTime(vo.getDeleteTime());
+ dto.setDeleted(vo.getDeleted());
+ return dto;
+ }
+
+ public static TbSysDeptRoleDTO toDeptRoleDTO(UserDeptRoleVO vo) {
+ if (vo == null) {
+ return null;
+ }
+ TbSysDeptRoleDTO dto = new TbSysDeptRoleDTO();
+ dto.setDeptId(vo.getDeptId());
+ dto.setRoleId(vo.getRoleId());
+ dto.setOptsn(vo.getOptsn());
+ dto.setCreator(vo.getCreator());
+ dto.setUpdater(vo.getUpdater());
+ dto.setDeptPath(vo.getDeptPath());
+ dto.setRemark(vo.getRemark());
+ dto.setCreateTime(vo.getCreateTime());
+ dto.setUpdateTime(vo.getUpdateTime());
+ dto.setDeleteTime(vo.getDeleteTime());
+ dto.setDeleted(vo.getDeleted());
+ return dto;
+ }
+
+ public static UserDeptRoleVO fromPermission(PermissionVO permissionVO) {
+ if (permissionVO == null) {
+ return null;
+ }
+ UserDeptRoleVO vo = new UserDeptRoleVO();
+ vo.setDeptId(permissionVO.getDeptId());
+ vo.setDeptName(permissionVO.getDeptName());
+ vo.setParentId(permissionVO.getDeptParentId());
+ vo.setDeptDescription(permissionVO.getDeptDescription());
+ vo.setRoleId(permissionVO.getRoleId());
+ vo.setRoleName(permissionVO.getRoleName());
+ vo.setRoleDescription(permissionVO.getRoleDescription());
+ vo.setScope(permissionVO.getRoleScope());
+ vo.setOwnerDeptId(permissionVO.getRoleOwnerDeptId());
+ vo.setRoleStatus(permissionVO.isRoleStatus());
+ vo.setOptsn(permissionVO.getOptsn());
+ vo.setCreator(permissionVO.getCreator());
+ vo.setUpdater(permissionVO.getUpdater());
+ vo.setDeptPath(permissionVO.getDeptPath());
+ vo.setRemark(permissionVO.getRemark());
+ vo.setCreateTime(permissionVO.getCreateTime());
+ vo.setUpdateTime(permissionVO.getUpdateTime());
+ vo.setDeleteTime(permissionVO.getDeleteTime());
+ vo.setDeleted(permissionVO.getDeleted());
+ return vo;
+ }
+}
\ No newline at end of file
diff --git a/urbanLifelineServ/apis/pom.xml b/urbanLifelineServ/apis/pom.xml
new file mode 100644
index 0000000..a4d2083
--- /dev/null
+++ b/urbanLifelineServ/apis/pom.xml
@@ -0,0 +1,82 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ urban-lifeline
+ 1.0.0
+
+
+ org.xyzh
+ apis
+ 1.0.0
+ pom
+
+ api-all
+ api-auth
+ api-file
+ api-message
+ api-log
+ api-system
+
+
+
+ 21
+ 21
+
+
+
+
+
+ org.xyzh.apis
+ api-all
+ ${urban-lifeline.version}
+
+
+ org.xyzh.apis
+ api-auth
+ ${urban-lifeline.version}
+
+
+ org.xyzh.apis
+ api-file
+ ${urban-lifeline.version}
+
+
+ org.xyzh.apis
+ api-message
+ ${urban-lifeline.version}
+
+
+ org.xyzh.apis
+ api-system
+ ${urban-lifeline.version}
+
+
+ org.xyzh.apis
+ api-log
+ ${urban-lifeline.version}
+
+
+
+
+
+
+ org.xyzh.common
+ common-core
+
+
+ org.xyzh.common
+ common-dto
+ ${urban-lifeline.version}
+
+
+
+ org.apache.dubbo
+ dubbo
+
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/auth/pom.xml b/urbanLifelineServ/auth/pom.xml
new file mode 100644
index 0000000..cc2d05f
--- /dev/null
+++ b/urbanLifelineServ/auth/pom.xml
@@ -0,0 +1,32 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ urban-lifeline
+ 1.0.0
+
+
+ org.xyzh
+ auth
+ ${urban-lifeline.version}
+
+
+ 21
+ 21
+
+
+
+
+ org.xyzh.apis
+ api-auth
+
+
+ org.xyzh.common
+ common-auth
+
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/AuthApp.java b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/AuthApp.java
new file mode 100644
index 0000000..6f51262
--- /dev/null
+++ b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/AuthApp.java
@@ -0,0 +1,24 @@
+package org.xyzh.auth;
+
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@EnableDubbo // 启用 Dubbo 服务
+@ComponentScan(basePackages = {
+ "org.xyzh.auth", // 当前auth模块
+ "org.xyzh.common" // 公共模块
+})
+public class AuthApp {
+ private static final Logger logger = LoggerFactory.getLogger(AuthApp.class);
+
+ public static void main(String[] args) {
+ logger.info("======================== AuthApp 启动中 =========================");
+ SpringApplication.run(AuthApp.class, args);
+ logger.info("======================== AuthApp 启动成功 =========================");
+ }
+}
\ No newline at end of file
diff --git a/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/config/OpenApiConfig.java b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/config/OpenApiConfig.java
new file mode 100644
index 0000000..798218f
--- /dev/null
+++ b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/config/OpenApiConfig.java
@@ -0,0 +1,58 @@
+package org.xyzh.auth.config;
+
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import io.swagger.v3.oas.models.servers.Server;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * OpenAPI 配置类 - Auth 服务
+ * 配置 Swagger/OpenAPI 文档,方便 Apifox 导入接口和对象进行测试
+ *
+ * @author yslg
+ */
+@Configuration
+public class OpenApiConfig {
+
+ @Bean
+ public OpenAPI authOpenAPI() {
+ return new OpenAPI()
+ .info(new Info()
+ .title("认证服务 API 文档")
+ .description("""
+ 认证服务接口文档,包括登录、登出、Token刷新等功能。
+
+ ## 使用说明
+ 1. 访问 Swagger UI: http://localhost:8081/urban-lifeline/auth/swagger-ui.html
+ 2. 访问 OpenAPI JSON: http://localhost:8081/urban-lifeline/auth/v3/api-docs
+ 3. 在 Apifox 中导入 OpenAPI JSON 进行接口测试
+ """)
+ .version("1.0.0")
+ .contact(new Contact()
+ .name("yslg")
+ .email("3401275564@qq.com"))
+ .license(new License()
+ .name("Apache 2.0")
+ .url("https://www.apache.org/licenses/LICENSE-2.0.html")))
+ .servers(List.of(
+ new Server().url("http://localhost:8081/urban-lifeline/auth").description("本地开发环境")
+ ))
+ .addSecurityItem(new SecurityRequirement().addList("Bearer Authentication"))
+ .components(new Components()
+ .addSecuritySchemes("Bearer Authentication",
+ new SecurityScheme()
+ .type(SecurityScheme.Type.HTTP)
+ .scheme("bearer")
+ .bearerFormat("JWT")
+ .description("请输入JWT Token,格式:Bearer {token}")));
+ }
+}
+
diff --git a/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/service/impl/AuthServiceImpl.java b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/service/impl/AuthServiceImpl.java
new file mode 100644
index 0000000..fed4332
--- /dev/null
+++ b/urbanLifelineServ/auth/src/main/java/org/xyzh/auth/service/impl/AuthServiceImpl.java
@@ -0,0 +1,62 @@
+package org.xyzh.auth.service.impl;
+
+import org.xyzh.api.auth.service.AuthService;
+import org.xyzh.common.core.domain.LoginDomain;
+import org.xyzh.common.core.domain.LoginParam;
+import org.xyzh.common.core.domain.ResultDomain;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * @description AuthServiceImpl.java文件描述 认证服务实现类
+ * @filename AuthServiceImpl.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-09
+ */
+public class AuthServiceImpl implements AuthService{
+
+ private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);
+
+ @Override
+ public ResultDomain getCaptcha(LoginParam arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ResultDomain getLoginByToken(String arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ResultDomain isTokenValid(String arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ResultDomain login(LoginParam arg0, HttpServletRequest arg1) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ResultDomain logout(HttpServletRequest arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ResultDomain refreshToken(HttpServletRequest arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+
+}
diff --git a/urbanLifelineServ/auth/src/main/resources/application.yml b/urbanLifelineServ/auth/src/main/resources/application.yml
new file mode 100644
index 0000000..1bc92a7
--- /dev/null
+++ b/urbanLifelineServ/auth/src/main/resources/application.yml
@@ -0,0 +1,36 @@
+server:
+ port: 8081
+ servlet:
+ context-path: /urban-lifeline/auth
+
+springdoc:
+ api-docs:
+ # 启用 API 文档
+ enabled: true
+ # API 文档路径
+ path: /v3/api-docs
+ swagger-ui:
+ # 启用 Swagger UI
+ enabled: true
+ # Swagger UI 路径
+ path: /swagger-ui.html
+ # 尝试请求超时时间(毫秒)
+ try-it-out-enabled: true
+ # 显示请求执行时间
+ show-common-extensions: true
+ # 显示请求头部
+ show-extensions: true
+ # 显示模型
+ show-request-duration: true
+ # 过滤开关
+ filter: true
+ # 标签排序
+ tags-sorter: alpha
+ # 操作排序
+ operations-sorter: alpha
+ # 分组配置(可选)
+ group-configs:
+ - group: 'default'
+ display-name: '认证服务 API'
+ paths-to-match: '/**'
+
diff --git a/urbanLifelineServ/common/common-all/pom.xml b/urbanLifelineServ/common/common-all/pom.xml
new file mode 100644
index 0000000..dcba574
--- /dev/null
+++ b/urbanLifelineServ/common/common-all/pom.xml
@@ -0,0 +1,45 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ common
+ 1.0.0
+
+
+ org.xyzh.common
+ common-all
+ ${urban-lifeline.version}
+ jar
+
+
+ 21
+ 21
+
+
+
+
+ org.xyzh.common
+ common-auth
+
+
+ org.xyzh.common
+ common-core
+
+
+ org.xyzh.common
+ common-dto
+
+
+ org.xyzh.common
+ common-redis
+
+
+ org.xyzh.common
+ common-utils
+
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/common/common-auth/pom.xml b/urbanLifelineServ/common/common-auth/pom.xml
new file mode 100644
index 0000000..55a8052
--- /dev/null
+++ b/urbanLifelineServ/common/common-auth/pom.xml
@@ -0,0 +1,123 @@
+
+
+ 4.0.0
+
+ org.xyzh
+ common
+ 1.0.0
+
+
+ org.xyzh.common
+ common-auth
+ ${urban-lifeline.version}
+ jar
+
+
+ 21
+ 21
+
+
+
+
+
+ org.xyzh.common
+ common-redis
+
+
+ org.xyzh.common
+ common-core
+
+
+ org.xyzh.common
+ common-utils
+
+
+ org.xyzh.common
+ common-dto
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ runtime
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ runtime
+
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jdbc
+
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+
\ No newline at end of file
diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/annotation/HttpLogin.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/annotation/HttpLogin.java
new file mode 100644
index 0000000..66acdaa
--- /dev/null
+++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/annotation/HttpLogin.java
@@ -0,0 +1,32 @@
+package org.xyzh.common.auth.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * @description HttpLogin.java文件描述 HTTP登录注解
+ * @filename HttpLogin.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-02
+ */
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface HttpLogin {
+
+ /**
+ * @description 是否必需,默认为true
+ * @return boolean
+ * @author yslg
+ * @since 2025-11-02
+ */
+ boolean required() default true;
+
+ /**
+ * @description 当token无效时的错误消息
+ * @return String
+ * @author yslg
+ * @since 2025-11-02
+ */
+ String message() default "用户未登录或登录已过期";
+}
diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/annotation/resovler/HttpLoginArgumentResolver.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/annotation/resovler/HttpLoginArgumentResolver.java
new file mode 100644
index 0000000..e9b01c4
--- /dev/null
+++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/annotation/resovler/HttpLoginArgumentResolver.java
@@ -0,0 +1,135 @@
+package org.xyzh.common.auth.annotation.resovler;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+import org.xyzh.common.auth.annotation.HttpLogin;
+import org.xyzh.common.auth.token.TokenParser;
+import org.xyzh.common.core.domain.LoginDomain;
+import org.xyzh.common.utils.NonUtils;
+import org.xyzh.redis.service.RedisService;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * @description HttpLoginArgumentResolver.java文件描述 HTTP登录参数解析器
+ * @filename HttpLoginArgumentResolver.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-02
+ */
+@Component
+public class HttpLoginArgumentResolver implements HandlerMethodArgumentResolver {
+
+ private final TokenParser tokenParser;
+ private final RedisService redisService;
+
+ /**
+ * 使用构造器注入,依赖TokenParser接口而非具体实现
+ * 这样避免了与auth模块的直接依赖,解决循环依赖问题
+ */
+ public HttpLoginArgumentResolver(TokenParser tokenParser,
+ RedisService redisService) {
+ this.tokenParser = tokenParser;
+ this.redisService = redisService;
+ }
+
+ private static final String TOKEN_PREFIX = "Bearer ";
+ private static final String REDIS_LOGIN_PREFIX = "login:token:";
+
+ @Override
+ public boolean supportsParameter(MethodParameter parameter) {
+ return parameter.hasParameterAnnotation(HttpLogin.class)
+ && LoginDomain.class.isAssignableFrom(parameter.getParameterType());
+ }
+
+ @Override
+ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
+ NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+
+ HttpLogin httpLogin = parameter.getParameterAnnotation(HttpLogin.class);
+ if (httpLogin == null) {
+ return null;
+ }
+
+ // 从请求头中获取token
+ String token = extractTokenFromRequest(webRequest);
+
+ if (NonUtils.isEmpty(token)) {
+ if (httpLogin.required()) {
+ throw new IllegalArgumentException(httpLogin.message());
+ }
+ return null;
+ }
+
+ try {
+ // 验证token格式和有效性
+ if (!tokenParser.validateToken(token, tokenParser.getUserIdFromToken(token))) {
+ if (httpLogin.required()) {
+ throw new IllegalArgumentException(httpLogin.message());
+ }
+ return null;
+ }
+
+ // 从Redis中获取LoginDomain
+ String userId = tokenParser.getUserIdFromToken(token);
+ String redisKey = REDIS_LOGIN_PREFIX + userId;
+ LoginDomain loginDomain = (LoginDomain) redisService.get(redisKey);
+
+ if (loginDomain == null) {
+ if (httpLogin.required()) {
+ throw new IllegalArgumentException(httpLogin.message());
+ }
+ return null;
+ }
+
+ // 更新token信息
+ loginDomain.setToken(token);
+ return loginDomain;
+
+ } catch (Exception e) {
+ if (httpLogin.required()) {
+ throw new IllegalArgumentException(httpLogin.message());
+ }
+ return null;
+ }
+ }
+
+ /**
+ * @description 从请求中提取token
+ * @param webRequest 请求对象
+ * @return String token
+ * @author yslg
+ * @since 2025-11-02
+ */
+ private String extractTokenFromRequest(NativeWebRequest webRequest) {
+ HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
+ if (request == null) {
+ return null;
+ }
+
+ // 优先从Authorization头获取
+ String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
+ if (NonUtils.isNotEmpty(authHeader) && authHeader.startsWith(TOKEN_PREFIX)) {
+ return authHeader.substring(TOKEN_PREFIX.length());
+ }
+
+ // 从请求参数中获取token
+ String token = request.getParameter("token");
+ if (NonUtils.isNotEmpty(token)) {
+ return token;
+ }
+
+ // 从请求头中获取token
+ token = request.getHeader("token");
+ if (NonUtils.isNotEmpty(token)) {
+ return token;
+ }
+
+ return null;
+ }
+}
diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/AuthProperties.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/AuthProperties.java
new file mode 100644
index 0000000..bc75ad6
--- /dev/null
+++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/AuthProperties.java
@@ -0,0 +1,160 @@
+package org.xyzh.common.auth.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 认证配置属性类
+ * 用于配置认证相关的属性,包括白名单路径
+ *
+ * @author yslg
+ */
+@Component
+@ConfigurationProperties(prefix = "urban-lifeline.auth")
+public class AuthProperties {
+
+ /**
+ * 是否启用认证过滤器
+ * 默认启用
+ */
+ private boolean enabled = true;
+
+ /**
+ * 登录接口路径
+ * 支持不同服务自定义登录地址
+ */
+ private String loginPath = "/urban-lifeline/auth/login";
+
+ /**
+ * 登出接口路径
+ */
+ private String logoutPath = "/urban-lifeline/auth/logout";
+
+ /**
+ * 验证码获取接口路径
+ */
+ private String captchaPath = "/urban-lifeline/auth/captcha";
+
+ /**
+ * 刷新 Token 接口路径
+ */
+ private String refreshPath = "/urban-lifeline/auth/refresh";
+
+ /**
+ * 通用白名单路径列表(非认证接口)
+ * 例如:Swagger、静态资源、健康检查等
+ */
+ private List whitelist = new ArrayList<>();
+
+ /**
+ * Token 请求头名称
+ * 默认: Authorization
+ */
+ private String tokenHeader = "Authorization";
+
+ /**
+ * Token 前缀
+ * 默认: Bearer
+ */
+ private String tokenPrefix = "Bearer ";
+
+ public AuthProperties() {
+ // 默认通用白名单:Swagger 及静态资源等
+ whitelist.add("/swagger-ui/**");
+ whitelist.add("/swagger-ui.html");
+ whitelist.add("/v3/api-docs/**");
+ whitelist.add("/webjars/**");
+ whitelist.add("/favicon.ico");
+ whitelist.add("/error");
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getLoginPath() {
+ return loginPath;
+ }
+
+ public void setLoginPath(String loginPath) {
+ this.loginPath = loginPath;
+ }
+
+ public String getLogoutPath() {
+ return logoutPath;
+ }
+
+ public void setLogoutPath(String logoutPath) {
+ this.logoutPath = logoutPath;
+ }
+
+ public String getCaptchaPath() {
+ return captchaPath;
+ }
+
+ public void setCaptchaPath(String captchaPath) {
+ this.captchaPath = captchaPath;
+ }
+
+ public String getRefreshPath() {
+ return refreshPath;
+ }
+
+ public void setRefreshPath(String refreshPath) {
+ this.refreshPath = refreshPath;
+ }
+
+ public List getWhitelist() {
+ return whitelist;
+ }
+
+ public void setWhitelist(List whitelist) {
+ this.whitelist = whitelist;
+ }
+
+ /**
+ * 认证相关接口路径集合(login / logout / captcha / refresh)
+ * 供 SecurityConfig 和 JwtAuthenticationFilter 统一放行
+ */
+ public List getAuthPaths() {
+ List authPaths = new ArrayList<>();
+ if (StringUtils.hasText(loginPath)) {
+ authPaths.add(loginPath);
+ }
+ if (StringUtils.hasText(logoutPath)) {
+ authPaths.add(logoutPath);
+ }
+ if (StringUtils.hasText(captchaPath)) {
+ authPaths.add(captchaPath);
+ }
+ if (StringUtils.hasText(refreshPath)) {
+ authPaths.add(refreshPath);
+ }
+ return authPaths;
+ }
+
+ public String getTokenHeader() {
+ return tokenHeader;
+ }
+
+ public void setTokenHeader(String tokenHeader) {
+ this.tokenHeader = tokenHeader;
+ }
+
+ public String getTokenPrefix() {
+ return tokenPrefix;
+ }
+
+ public void setTokenPrefix(String tokenPrefix) {
+ this.tokenPrefix = tokenPrefix;
+ }
+}
+
diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/SecurityConfig.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/SecurityConfig.java
new file mode 100644
index 0000000..95891da
--- /dev/null
+++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/SecurityConfig.java
@@ -0,0 +1,55 @@
+package org.xyzh.common.auth.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.xyzh.common.auth.filter.JwtAuthenticationFilter;
+import org.xyzh.common.auth.token.TokenParser;
+import org.xyzh.redis.service.RedisService;
+
+@Configuration
+@EnableMethodSecurity
+public class SecurityConfig {
+
+ @Bean
+ public JwtAuthenticationFilter jwtAuthenticationFilter(TokenParser tokenParser,
+ AuthProperties authProperties,
+ RedisService redisService) {
+ return new JwtAuthenticationFilter(tokenParser, authProperties, redisService);
+ }
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http,
+ AuthProperties authProperties,
+ JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
+ http
+ .csrf(csrf -> csrf.disable())
+ .formLogin(form -> form.disable())
+ .httpBasic(basic -> basic.disable())
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .authorizeHttpRequests(authz -> {
+ // 认证接口放行(login / logout / captcha / refresh)
+ if (authProperties.getAuthPaths() != null) {
+ authProperties.getAuthPaths().forEach(path -> authz.requestMatchers(path).permitAll());
+ }
+
+ // 通用白名单放行(Swagger、静态资源等)
+ if (authProperties.getWhitelist() != null) {
+ authProperties.getWhitelist().forEach(path -> authz.requestMatchers(path).permitAll());
+ }
+
+ authz
+ .requestMatchers("/error", "/favicon.ico").permitAll()
+ .anyRequest().authenticated();
+ })
+ .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+}
+
+
diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/WebMvcConfig.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/WebMvcConfig.java
new file mode 100644
index 0000000..0484f06
--- /dev/null
+++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/WebMvcConfig.java
@@ -0,0 +1,34 @@
+package org.xyzh.common.auth.config;
+
+import java.util.List;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.xyzh.common.auth.annotation.resovler.HttpLoginArgumentResolver;
+
+/**
+ * @description WebMvcConfig.java文件描述 WebMVC配置类
+ * @filename WebMvcConfig.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-02
+ */
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+
+ private final HandlerMethodArgumentResolver httpLoginArgumentResolver;
+
+ /**
+ * 使用构造器注入
+ * 通过接口抽象解决了循环依赖问题,不再需要@Lazy注解
+ */
+ public WebMvcConfig(HttpLoginArgumentResolver httpLoginArgumentResolver) {
+ this.httpLoginArgumentResolver = httpLoginArgumentResolver;
+ }
+
+ @Override
+ public void addArgumentResolvers(List resolvers) {
+ resolvers.add(httpLoginArgumentResolver);
+ }
+}
diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/contants/AuthContants.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/contants/AuthContants.java
new file mode 100644
index 0000000..a8c9676
--- /dev/null
+++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/contants/AuthContants.java
@@ -0,0 +1,43 @@
+package org.xyzh.common.auth.contants;
+
+/**
+ * @description 认证相关常量类
+ * @filename AuthContants.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-09
+ */
+public class AuthContants {
+
+ /**
+ * 用户ID请求属性键
+ */
+ public static final String USER_ID_ATTRIBUTE = "userId";
+
+ /**
+ * 用户名请求属性键
+ */
+ public static final String USERNAME_ATTRIBUTE = "username";
+
+ /**
+ * Token请求属性键
+ */
+ public static final String TOKEN_ATTRIBUTE = "token";
+
+ /**
+ * JWT Claims 中的用户名键
+ */
+ public static final String CLAIMS_USERNAME_KEY = "username";
+
+ /**
+ * JWT Claims 中的用户ID键
+ */
+ public static final String CLAIMS_USER_ID_KEY = "userId";
+
+ /**
+ * 私有构造函数,防止实例化
+ */
+ private AuthContants() {
+ throw new UnsupportedOperationException("常量类不允许实例化");
+ }
+}
diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/filter/JwtAuthenticationFilter.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/filter/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..562d93a
--- /dev/null
+++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/filter/JwtAuthenticationFilter.java
@@ -0,0 +1,237 @@
+package org.xyzh.common.auth.filter;
+
+import com.alibaba.fastjson2.JSON;
+import io.jsonwebtoken.Claims;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.lang.NonNull;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.xyzh.common.auth.config.AuthProperties;
+import org.xyzh.common.auth.contants.AuthContants;
+import org.xyzh.common.auth.token.TokenParser;
+import org.xyzh.common.core.domain.ResultDomain;
+import org.xyzh.common.core.domain.LoginDomain;
+import org.xyzh.common.dto.sys.TbSysPermissionDTO;
+import org.xyzh.redis.service.RedisService;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * @description JWT认证过滤器,用于检测用户请求是否登录,支持白名单配置
+ * @filename JwtAuthenticationFilter.java
+ * @author yslg
+ * @copyright yslg
+ * @since 2025-11-09
+ */
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+ private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+
+ private final TokenParser tokenParser;
+ private final AuthProperties authProperties;
+ private final AntPathMatcher pathMatcher = new AntPathMatcher();
+ private final RedisService redisService;
+ private static final String REDIS_LOGIN_PREFIX = "login:token:";
+
+ public JwtAuthenticationFilter(TokenParser tokenParser, AuthProperties authProperties) {
+ this.tokenParser = tokenParser;
+ this.authProperties = authProperties;
+ this.redisService = null; // 占位,使用另一个构造函数注入
+ }
+
+ public JwtAuthenticationFilter(TokenParser tokenParser, AuthProperties authProperties, RedisService redisService) {
+ this.tokenParser = tokenParser;
+ this.authProperties = authProperties;
+ this.redisService = redisService;
+ }
+
+ @Override
+ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain)
+ throws ServletException, IOException {
+
+ // 如果认证过滤器未启用,直接放行
+ if (!authProperties.isEnabled()) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+
+ String requestPath = request.getRequestURI();
+ if (requestPath == null) {
+ requestPath = "";
+ }
+
+ // 去掉 context-path,仅保留业务路径用于白名单匹配
+ String contextPath = request.getContextPath();
+ if (contextPath != null && !contextPath.isEmpty() && requestPath.startsWith(contextPath)) {
+ requestPath = requestPath.substring(contextPath.length());
+ }
+
+ // 检查是否在白名单中
+ if (isWhitelisted(requestPath)) {
+ log.debug("请求路径在白名单中,跳过认证: {}", requestPath);
+ filterChain.doFilter(request, response);
+ return;
+ }
+
+ // 从请求头获取Token
+ String token = extractToken(request);
+
+ if (!StringUtils.hasText(token)) {
+ log.warn("请求缺少Token: {}", requestPath);
+ handleUnauthorized(response, "未提供认证令牌,请先登录");
+ return;
+ }
+
+ try {
+ // 验证Token
+ if (tokenParser.isTokenExpired(token)) {
+ log.warn("Token已过期: {}", requestPath);
+ handleUnauthorized(response, "认证令牌已过期,请重新登录");
+ return;
+ }
+
+ // 获取用户ID
+ String userId = tokenParser.getUserIdFromToken(token);
+ if (!StringUtils.hasText(userId)) {
+ log.warn("Token中未找到用户ID: {}", requestPath);
+ handleUnauthorized(response, "认证令牌无效");
+ return;
+ }
+
+ // 验证Token有效性
+ if (!tokenParser.validateToken(token, userId)) {
+ log.warn("Token验证失败: userId={}, path={}", userId, requestPath);
+ handleUnauthorized(response, "认证令牌验证失败");
+ return;
+ }
+
+ // 将用户信息放入请求属性中,供后续使用
+ Claims claims = tokenParser.getAllClaimsFromToken(token);
+ request.setAttribute(AuthContants.USER_ID_ATTRIBUTE, userId);
+ request.setAttribute(AuthContants.USERNAME_ATTRIBUTE, claims.get(AuthContants.CLAIMS_USERNAME_KEY, String.class));
+ request.setAttribute(AuthContants.TOKEN_ATTRIBUTE, token);
+
+ // 从Redis加载 LoginDomain,并将权限装配到 Spring Security 上下文
+ if (redisService != null) {
+ Object obj = redisService.get(REDIS_LOGIN_PREFIX + userId);
+ if (obj instanceof LoginDomain loginDomain) {
+ // 组装权限码 authorities(已存在)
+ List permAuthorities = null;
+ if (loginDomain.getUserPermissions() != null) {
+ permAuthorities = loginDomain.getUserPermissions().stream()
+ .filter(Objects::nonNull)
+ .map(TbSysPermissionDTO::getCode)
+ .filter(StringUtils::hasText)
+ .map(SimpleGrantedAuthority::new)
+ .toList();
+ }
+
+ // 组装角色 authorities(关键:ROLE_ 前缀)
+ List roleAuthorities = null;
+ if (loginDomain.getUserRoles() != null) {
+ roleAuthorities = loginDomain.getUserRoles().stream()
+ .filter(Objects::nonNull)
+ .map(r -> r.getRoleId()) // 若有角色code/名称,可替换为对应字段
+ .filter(StringUtils::hasText)
+ .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
+ .toList();
+ }
+
+ // 合并权限与角色
+ List authorities = Stream
+ .concat(
+ permAuthorities != null ? permAuthorities.stream() : Stream.empty(),
+ roleAuthorities != null ? roleAuthorities.stream() : Stream.empty()
+ )
+ .toList();
+ UsernamePasswordAuthenticationToken authentication =
+ new UsernamePasswordAuthenticationToken(loginDomain, null, authorities);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ }
+
+ log.debug("Token验证成功: userId={}, path={}", userId, requestPath);
+
+ // 继续执行过滤器链
+ filterChain.doFilter(request, response);
+
+ } catch (Exception e) {
+ log.error("Token解析或验证异常: path={}, error={}", requestPath, e.getMessage(), e);
+ handleUnauthorized(response, "认证令牌解析失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 检查路径是否在白名单中
+ */
+ private boolean isWhitelisted(@NonNull String path) {
+ // 1. 先检查认证相关接口(login / logout / captcha / refresh)
+ if (authProperties.getAuthPaths() != null) {
+ for (String pattern : authProperties.getAuthPaths()) {
+ if (pattern != null && pathMatcher.match(pattern, path)) {
+ return true;
+ }
+ }
+ }
+
+ // 2. 再检查通用白名单
+ if (authProperties.getWhitelist() != null) {
+ for (String pattern : authProperties.getWhitelist()) {
+ if (pattern != null && pathMatcher.match(pattern, path)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 从请求头中提取Token
+ */
+ private String extractToken(HttpServletRequest request) {
+ String header = request.getHeader(authProperties.getTokenHeader());
+
+ if (!StringUtils.hasText(header)) {
+ return null;
+ }
+
+ // 支持 Bearer 前缀
+ String prefix = authProperties.getTokenPrefix();
+ if (StringUtils.hasText(prefix) && header.startsWith(prefix)) {
+ return header.substring(prefix.length()).trim();
+ }
+
+ // 也支持直接传递Token(不带前缀)
+ return header.trim();
+ }
+
+ /**
+ * 处理未授权响应
+ */
+ private void handleUnauthorized(HttpServletResponse response, String message) throws IOException {
+ response.setStatus(HttpStatus.UNAUTHORIZED.value());
+ response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ response.setCharacterEncoding(StandardCharsets.UTF_8.name());
+
+ ResultDomain