Compare commits
7 Commits
9cb4844be4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 68c91b4ba3 | |||
| 133209691e | |||
| a8233ceb72 | |||
| ab8be1a832 | |||
| 917e9a517a | |||
| 9a3547b70b | |||
| b3200f8858 |
@@ -1,225 +0,0 @@
|
||||
# 数据库初始化说明
|
||||
|
||||
## 概述
|
||||
|
||||
本目录包含城市生命线AI数智化平台的数据库初始化脚本,用于创建表结构和插入基础数据。
|
||||
|
||||
## 文件说明
|
||||
|
||||
### 表结构创建脚本
|
||||
|
||||
| 文件名 | 说明 |
|
||||
|--------|------|
|
||||
| `createDB.sql` | 创建数据库 |
|
||||
| `createTablePermission.sql` | 创建权限相关表(部门、角色、权限、视图、ACL等) |
|
||||
| `createTableUser.sql` | 创建用户相关表(用户、用户信息、登录日志) |
|
||||
| `createTableFile.sql` | 创建文件管理表 |
|
||||
| `createTableMessage.sql` | 创建消息通知表 |
|
||||
| `createTableLog.sql` | 创建日志表 |
|
||||
| `createTableConfig.sql` | 创建配置管理表 |
|
||||
| `createTableKnowledge.sql` | 创建知识库表 |
|
||||
| `createTableBidding.sql` | 创建招投标业务表 |
|
||||
| `createTableCustomerService.sql` | 创建智能客服表 |
|
||||
| `createTableAgent.sql` | 创建智能体表(暂不启用) |
|
||||
|
||||
### 数据初始化脚本
|
||||
|
||||
| 文件名 | 说明 | 是否必需 |
|
||||
|--------|------|----------|
|
||||
| `initDataPermission.sql` | 初始化权限基础数据(部门、角色、权限、视图、模块) | ✅ 必需 |
|
||||
| `initDataUser.sql` | 初始化用户数据(管理员账户、演示用户) | ✅ 必需 |
|
||||
| `initDataMessage.sql` | 初始化消息渠道配置和模板 | ✅ 必需 |
|
||||
| `initDataConfig.sql` | 初始化系统配置参数 | ✅ 必需 |
|
||||
|
||||
### 总入口脚本
|
||||
|
||||
- `initAll.sql` - 一键执行所有建表和初始化脚本
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 方式一:使用总入口脚本(推荐)
|
||||
|
||||
```bash
|
||||
# 进入 PostgreSQL 命令行
|
||||
psql -U postgres
|
||||
|
||||
# 执行总初始化脚本
|
||||
\i /path/to/urbanLifelineServ/.bin/database/postgres/sql/initAll.sql
|
||||
```
|
||||
|
||||
### 方式二:分步执行
|
||||
|
||||
#### 第一步:创建表结构
|
||||
|
||||
```bash
|
||||
# 创建数据库
|
||||
\i createDB.sql
|
||||
|
||||
# 创建各模块表结构
|
||||
\i createTablePermission.sql
|
||||
\i createTableUser.sql
|
||||
\i createTableFile.sql
|
||||
\i createTableMessage.sql
|
||||
\i createTableLog.sql
|
||||
\i createTableConfig.sql
|
||||
\i createTableKnowledge.sql
|
||||
\i createTableBidding.sql
|
||||
\i createTableCustomerService.sql
|
||||
```
|
||||
|
||||
#### 第二步:初始化基础数据
|
||||
|
||||
```bash
|
||||
# 按顺序执行初始化脚本
|
||||
\i initDataPermission.sql
|
||||
\i initDataUser.sql
|
||||
\i initDataMessage.sql
|
||||
\i initDataConfig.sql
|
||||
```
|
||||
|
||||
## 初始化数据说明
|
||||
|
||||
### 1. 权限基础数据 (initDataPermission.sql)
|
||||
|
||||
**初始化内容:**
|
||||
- **根部门**:dept_root
|
||||
- **全局角色**:
|
||||
- 超级管理员 (role_super_admin) - 拥有所有权限
|
||||
- 系统管理员 (role_system_admin) - 拥有系统管理权限
|
||||
- 普通用户 (role_user) - 基础查看和操作权限
|
||||
- 访客 (role_guest) - 仅查看权限
|
||||
|
||||
- **系统模块**:
|
||||
- 系统管理 (mod_system)
|
||||
- 文件管理 (mod_file)
|
||||
- 消息通知 (mod_message)
|
||||
- 配置管理 (mod_config)
|
||||
- 知识库 (mod_knowledge)
|
||||
- 招投标 (mod_bidding)
|
||||
- 智能客服 (mod_customer_service)
|
||||
|
||||
- **系统权限**:30+ 个基础权限,涵盖用户、角色、部门、权限、文件、消息、配置管理
|
||||
|
||||
- **系统视图(菜单)**:
|
||||
- 系统管理菜单及其子菜单
|
||||
- 业务管理菜单框架
|
||||
|
||||
### 2. 用户数据 (initDataUser.sql)
|
||||
|
||||
**初始化账户:**
|
||||
|
||||
| 用户 | 账号 | 默认密码 | 角色 | 说明 |
|
||||
|------|------|----------|------|------|
|
||||
| 系统管理员 | admin | admin123 | 超级管理员 | 拥有所有权限 |
|
||||
| 演示用户 | demo | admin123 | 普通用户 | 用于演示和测试 |
|
||||
|
||||
**⚠️ 安全提示:**
|
||||
- 生产环境部署前,必须修改默认密码
|
||||
- 密码使用 bcrypt 加密存储
|
||||
- 建议删除演示用户账户
|
||||
|
||||
### 3. 消息渠道配置 (initDataMessage.sql)
|
||||
|
||||
**初始化内容:**
|
||||
- **消息渠道**(默认禁用,需配置后启用):
|
||||
- 应用内消息 (app) - 默认启用
|
||||
- 短信通知 (sms)
|
||||
- 邮件通知 (email)
|
||||
- 微信公众号 (wechat_official_account)
|
||||
- 微信小程序 (wechat_applet)
|
||||
- 钉钉通知 (dingtalk)
|
||||
|
||||
- **消息模板**:
|
||||
- 用户注册欢迎
|
||||
- 密码重置通知
|
||||
- 系统维护通知
|
||||
- 工单创建通知
|
||||
- 招标公告发布
|
||||
|
||||
### 4. 系统配置 (initDataConfig.sql)
|
||||
|
||||
**初始化配置分组:**
|
||||
- **站点配置**:站点名称、Logo、ICP备案
|
||||
- **国际化**:默认语言、时区
|
||||
- **安全认证**:密码策略、JWT过期时间、会话超时、注册开关
|
||||
- **存储上传**:最大上传大小、存储后端、存储路径
|
||||
- **通知配置**:邮件SMTP、短信服务商
|
||||
- **日志审计**:日志级别、审计日志保留天数
|
||||
- **平台特性**:维护模式、ACL策略开关
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **执行顺序**:必须按照 `initAll.sql` 中的顺序执行,先创建表结构,再插入数据
|
||||
2. **依赖关系**:初始化数据脚本有依赖关系,必须按顺序执行
|
||||
3. **数据库权限**:执行脚本需要具有创建数据库、创建表、插入数据的权限
|
||||
4. **字符编码**:确保数据库使用 UTF-8 编码
|
||||
5. **时区设置**:建议数据库时区设置为 Asia/Shanghai 或 UTC
|
||||
|
||||
## 验证初始化结果
|
||||
|
||||
执行以下 SQL 验证初始化是否成功:
|
||||
|
||||
```sql
|
||||
-- 检查表是否创建成功
|
||||
SELECT schemaname, tablename
|
||||
FROM pg_tables
|
||||
WHERE schemaname IN ('sys', 'file', 'message', 'config')
|
||||
ORDER BY schemaname, tablename;
|
||||
|
||||
-- 检查角色数量
|
||||
SELECT COUNT(*) as role_count FROM sys.tb_sys_role WHERE deleted = false;
|
||||
-- 预期结果:4
|
||||
|
||||
-- 检查权限数量
|
||||
SELECT COUNT(*) as permission_count FROM sys.tb_sys_permission WHERE deleted = false;
|
||||
-- 预期结果:30+
|
||||
|
||||
-- 检查用户数量
|
||||
SELECT COUNT(*) as user_count FROM sys.tb_sys_user WHERE deleted = false;
|
||||
-- 预期结果:2
|
||||
|
||||
-- 检查消息渠道数量
|
||||
SELECT COUNT(*) as channel_count FROM message.tb_message_channel WHERE deleted = false;
|
||||
-- 预期结果:6
|
||||
|
||||
-- 检查系统配置数量
|
||||
SELECT COUNT(*) as config_count FROM config.tb_sys_config WHERE deleted = false;
|
||||
-- 预期结果:20+
|
||||
```
|
||||
|
||||
## 重置数据库
|
||||
|
||||
如需重新初始化数据库,可以执行以下操作:
|
||||
|
||||
```sql
|
||||
-- 删除所有 schema(谨慎操作!)
|
||||
DROP SCHEMA IF EXISTS sys CASCADE;
|
||||
DROP SCHEMA IF EXISTS file CASCADE;
|
||||
DROP SCHEMA IF EXISTS message CASCADE;
|
||||
DROP SCHEMA IF EXISTS config CASCADE;
|
||||
DROP SCHEMA IF EXISTS knowledge CASCADE;
|
||||
DROP SCHEMA IF EXISTS bidding CASCADE;
|
||||
DROP SCHEMA IF EXISTS customer_service CASCADE;
|
||||
DROP SCHEMA IF EXISTS log CASCADE;
|
||||
|
||||
-- 然后重新执行 initAll.sql
|
||||
\i initAll.sql
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 执行初始化脚本报错 "relation already exists"
|
||||
A: 表已存在,可以选择删除对应的表或 schema 后重新执行
|
||||
|
||||
### Q: 如何修改管理员默认密码?
|
||||
A: 修改 `initDataUser.sql` 中的 password 字段,使用 bcrypt 加密后的密码哈希
|
||||
|
||||
### Q: 如何自定义初始化数据?
|
||||
A: 直接修改对应的 `initData*.sql` 文件,按照现有格式添加或修改数据
|
||||
|
||||
### Q: 业务表(知识库、招投标、客服)需要初始化数据吗?
|
||||
A: 不需要,这些表的数据在系统运行时动态产生
|
||||
|
||||
## 联系支持
|
||||
|
||||
如有问题,请联系技术支持团队。
|
||||
@@ -1,11 +1,11 @@
|
||||
|
||||
-- 删除已存在的数据库(如果存在)
|
||||
DROP DATABASE IF EXISTS urban-lifeline;
|
||||
DROP DATABASE IF EXISTS urban_lifeline;
|
||||
|
||||
-- 创建新数据库,使用 UTF8 编码,并设置适合中文的排序规则
|
||||
-- 使用 template0 确保干净的数据库模板
|
||||
-- zh_CN.UTF-8 支持中文字符排序和比较
|
||||
CREATE DATABASE urban-lifeline
|
||||
CREATE DATABASE urban_lifeline
|
||||
ENCODING 'UTF8'
|
||||
TEMPLATE template0
|
||||
LC_COLLATE 'zh_CN.UTF-8'
|
||||
|
||||
@@ -11,7 +11,7 @@ CREATE TABLE config.tb_sys_config (
|
||||
description VARCHAR(255) NOT NULL, -- 配置描述
|
||||
re JSON DEFAULT NULL, -- 正则表达式校验规则
|
||||
options JSON DEFAULT NULL, -- 可选项,render_type为select、checkbox、radio时使用
|
||||
group VARCHAR(255) NOT NULL, -- 配置组
|
||||
"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:禁用
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
-- ====================================================
|
||||
-- 定时任务表
|
||||
-- ====================================================
|
||||
CREATE SCHEMA IF NOT EXISTS crontab;
|
||||
DROP TABLE IF EXISTS crontab.tb_crontab_task CASCADE;
|
||||
CREATE TABLE crontab.tb_crontab_task (
|
||||
id VARCHAR(64) NOT NULL,
|
||||
task_id VARCHAR(64) NOT NULL,
|
||||
task_name VARCHAR(100) NOT NULL,
|
||||
task_group VARCHAR(50) NOT NULL DEFAULT 'DEFAULT',
|
||||
meta_id VARCHAR(64) NOT NULL,
|
||||
default_recipient SMALLINT NOT NULL DEFAULT 0, -- 是否使用默认接收人(0:否 1:是)
|
||||
bean_name VARCHAR(100) NOT NULL,
|
||||
method_name VARCHAR(100) NOT NULL,
|
||||
method_params VARCHAR(500) DEFAULT NULL,
|
||||
cron_expression VARCHAR(100) NOT NULL,
|
||||
status SMALLINT NOT NULL DEFAULT 0, -- 任务状态(0:暂停 1:运行中)
|
||||
description VARCHAR(500) DEFAULT NULL,
|
||||
concurrent SMALLINT NOT NULL DEFAULT 0, -- 是否允许并发执行(0:否 1:是)
|
||||
misfire_policy SMALLINT NOT NULL DEFAULT 1, -- 错过执行策略(1:立即执行 2:执行一次 3:放弃执行)
|
||||
creator VARCHAR(64) DEFAULT NULL,
|
||||
updater VARCHAR(64) DEFAULT NULL,
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
update_time TIMESTAMPTZ DEFAULT NULL,
|
||||
delete_time TIMESTAMPTZ DEFAULT NULL,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0, -- 是否删除(0:否 1:是)
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_task_name ON crontab.tb_crontab_task(task_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_bean_name ON crontab.tb_crontab_task(bean_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_status ON crontab.tb_crontab_task(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_deleted ON crontab.tb_crontab_task(deleted);
|
||||
|
||||
COMMENT ON TABLE crontab.tb_crontab_task IS '定时任务配置表';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.id IS '主键ID';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.task_id IS '任务ID';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.task_name IS '任务名称';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.task_group IS '任务分组';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.meta_id IS '任务元数据ID';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.default_recipient IS '是否使用默认接收人(0:否 1:是)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.bean_name IS 'Bean名称';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.method_name IS '方法名称';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.method_params IS '方法参数';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.cron_expression IS 'Cron表达式';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.status IS '任务状态(0:暂停 1:运行中)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.description IS '任务描述';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.concurrent IS '是否允许并发执行(0:否 1:是)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.misfire_policy IS '错过执行策略(1:立即执行 2:执行一次 3:放弃执行)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.creator IS '创建者';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.updater IS '更新者';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.create_time IS '创建时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.update_time IS '更新时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.delete_time IS '删除时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task.deleted IS '是否删除(0:否 1:是)';
|
||||
|
||||
-- ====================================================
|
||||
-- 定时任务执行日志表
|
||||
-- ====================================================
|
||||
DROP TABLE IF EXISTS crontab.tb_crontab_log CASCADE;
|
||||
CREATE TABLE crontab.tb_crontab_log (
|
||||
id VARCHAR(64) NOT NULL,
|
||||
task_id VARCHAR(64) NOT NULL,
|
||||
task_name VARCHAR(100) NOT NULL,
|
||||
task_group VARCHAR(50) NOT NULL DEFAULT 'DEFAULT',
|
||||
bean_name VARCHAR(100) NOT NULL,
|
||||
method_name VARCHAR(100) NOT NULL,
|
||||
method_params VARCHAR(500) DEFAULT NULL,
|
||||
execute_status SMALLINT NOT NULL, -- 执行状态(0:失败 1:成功)
|
||||
execute_message TEXT DEFAULT NULL,
|
||||
exception_info TEXT DEFAULT NULL,
|
||||
start_time TIMESTAMPTZ NOT NULL,
|
||||
end_time TIMESTAMPTZ DEFAULT NULL,
|
||||
execute_duration INT DEFAULT NULL,
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
update_time TIMESTAMPTZ DEFAULT NULL,
|
||||
delete_time TIMESTAMPTZ DEFAULT NULL,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0, -- 是否删除(0:否 1:是)
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_task_id ON crontab.tb_crontab_log(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_log_task_name ON crontab.tb_crontab_log(task_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_execute_status ON crontab.tb_crontab_log(execute_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_start_time ON crontab.tb_crontab_log(start_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_log_deleted ON crontab.tb_crontab_log(deleted);
|
||||
|
||||
COMMENT ON TABLE crontab.tb_crontab_log IS '定时任务执行日志表';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.id IS '主键ID';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.task_id IS '任务ID';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.task_name IS '任务名称';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.task_group IS '任务分组';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.bean_name IS 'Bean名称';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.method_name IS '方法名称';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.method_params IS '方法参数';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.execute_status IS '执行状态(0:失败 1:成功)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.execute_message IS '执行结果信息';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.exception_info IS '异常信息';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.start_time IS '开始时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.end_time IS '结束时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.execute_duration IS '执行时长(毫秒)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.create_time IS '创建时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.update_time IS '更新时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.delete_time IS '删除时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_log.deleted IS '是否删除(0:否 1:是)';
|
||||
|
||||
-- ====================================================
|
||||
-- 定时任务元数据表(存储爬虫任务的元数据配置)
|
||||
-- ====================================================
|
||||
DROP TABLE IF EXISTS crontab.tb_crontab_task_meta CASCADE;
|
||||
CREATE TABLE crontab.tb_crontab_task_meta (
|
||||
id VARCHAR(64) NOT NULL,
|
||||
meta_id VARCHAR(64) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description VARCHAR(500) DEFAULT NULL,
|
||||
category VARCHAR(50) NOT NULL,
|
||||
bean_name VARCHAR(100) NOT NULL,
|
||||
method_name VARCHAR(100) NOT NULL,
|
||||
script_path VARCHAR(255) DEFAULT NULL,
|
||||
param_schema TEXT DEFAULT NULL,
|
||||
auto_publish SMALLINT NOT NULL DEFAULT 0, -- 是否自动发布
|
||||
sort_order INT DEFAULT 0,
|
||||
creator VARCHAR(64) DEFAULT NULL,
|
||||
updater VARCHAR(64) DEFAULT NULL,
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
update_time TIMESTAMPTZ DEFAULT NULL,
|
||||
delete_time TIMESTAMPTZ DEFAULT NULL,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0, -- 是否删除(0:否 1:是)
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (meta_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_category ON crontab.tb_crontab_task_meta(category);
|
||||
CREATE INDEX IF NOT EXISTS idx_meta_deleted ON crontab.tb_crontab_task_meta(deleted);
|
||||
|
||||
COMMENT ON TABLE crontab.tb_crontab_task_meta IS '定时任务元数据表';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.id IS '主键ID';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.meta_id IS '元数据ID';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.name IS '任务名称';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.description IS '任务描述';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.category IS '任务分类(如:人民日报新闻爬取)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.bean_name IS 'Bean名称(执行器类名)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.method_name IS '执行方法名';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.script_path IS 'Python脚本路径(相对于basePath)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.param_schema IS '参数模板(JSON格式,定义参数名、类型、描述、默认值等)';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.auto_publish IS '是否自动发布';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.sort_order IS '排序号';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.creator IS '创建者';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.updater IS '更新者';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.create_time IS '创建时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.update_time IS '更新时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.delete_time IS '删除时间';
|
||||
COMMENT ON COLUMN crontab.tb_crontab_task_meta.deleted IS '是否删除(0:否 1:是)';
|
||||
@@ -54,9 +54,8 @@ CREATE TABLE knowledge.tb_knowledge_document (
|
||||
file_path VARCHAR(500), -- 文件路径
|
||||
file_size BIGINT, -- 文件大小(字节)
|
||||
mime_type VARCHAR(100), -- MIME类型
|
||||
version INTEGER DEFAULT 1, -- 版本号(自动递增)
|
||||
root_doc_id VARCHAR(50), -- 根文档ID(版本组标识)
|
||||
is_current BOOLEAN DEFAULT true, -- 是否当前使用的版本
|
||||
version INTEGER DEFAULT 1, -- 文档版本号(仅做记录)
|
||||
root_doc_id VARCHAR(50), -- 根文档ID(版本组标识,保留用于整体版本管理)
|
||||
tags TEXT[], -- 文档标签数组
|
||||
keywords TEXT[], -- 关键词数组(AI提取)
|
||||
embedding_status VARCHAR(20) DEFAULT 'pending', -- 向量化状态:pending-待处理/processing-处理中/completed-完成/failed-失败
|
||||
@@ -74,8 +73,7 @@ CREATE TABLE knowledge.tb_knowledge_document (
|
||||
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
|
||||
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
|
||||
PRIMARY KEY (doc_id),
|
||||
UNIQUE (optsn),
|
||||
FOREIGN KEY (knowledge_id) REFERENCES knowledge.tb_knowledge_base(knowledge_id)
|
||||
UNIQUE (optsn)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_doc_kb ON knowledge.tb_knowledge_document(knowledge_id) WHERE deleted = false;
|
||||
@@ -83,13 +81,12 @@ CREATE INDEX idx_doc_service ON knowledge.tb_knowledge_document(service_type) WH
|
||||
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;
|
||||
CREATE INDEX idx_doc_root_current ON knowledge.tb_knowledge_document(root_doc_id, is_current) WHERE deleted = false;
|
||||
CREATE INDEX idx_doc_root ON knowledge.tb_knowledge_document(root_doc_id) WHERE deleted = false;
|
||||
|
||||
COMMENT ON TABLE knowledge.tb_knowledge_document IS '知识文档表';
|
||||
COMMENT ON TABLE knowledge.tb_knowledge_document IS '知识文档表(文档级元数据,版本控制在chunk级别)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_document.service_type IS '服务类型(从知识库继承,用于服务间隔离)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_document.version IS '版本号(整数,每次创建新版本自动+1)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_document.root_doc_id IS '根文档ID(同一文档的所有版本共享此ID,首次上传时等于doc_id)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_document.is_current IS '是否当前使用的版本(每个root_doc_id只有一个is_current=true)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_document.version IS '文档版本号(仅做记录,实际版本控制在chunk级别)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_document.root_doc_id IS '根文档ID(用于文档整体版本管理,可选)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_document.embedding_status IS '向量化状态:pending/processing/completed/failed';
|
||||
|
||||
-- 知识文档片段表(用于RAG检索)
|
||||
@@ -104,7 +101,9 @@ CREATE TABLE knowledge.tb_knowledge_chunk (
|
||||
content_length INTEGER, -- 内容长度
|
||||
embedding vector(1536), -- 向量嵌入(假设使用OpenAI 1536维)
|
||||
chunk_type VARCHAR(20) DEFAULT 'text', -- 片段类型:text-文本/table-表格/image-图片
|
||||
version INTEGER DEFAULT 1, -- 分段版本号(用于乐观锁,每次编辑+1)
|
||||
version INTEGER DEFAULT 1, -- chunk版本号(每次修改自动+1)
|
||||
root_chunk_id VARCHAR(50), -- 根chunk ID(同一chunk的不同版本共享此ID)
|
||||
is_current BOOLEAN DEFAULT true, -- 是否当前使用的版本
|
||||
position_info JSONB, -- 位置信息(页码、坐标等)
|
||||
metadata JSONB, -- 片段元数据
|
||||
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
|
||||
@@ -113,19 +112,21 @@ CREATE TABLE knowledge.tb_knowledge_chunk (
|
||||
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 (knowledge_id) REFERENCES knowledge.tb_knowledge_base(knowledge_id)
|
||||
UNIQUE (optsn)
|
||||
);
|
||||
|
||||
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(knowledge_id) WHERE deleted = false;
|
||||
-- 向量检索索引(需要安装pgvector扩展)
|
||||
-- CREATE INDEX idx_chunk_embedding ON knowledge.tb_knowledge_chunk USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
|
||||
CREATE INDEX idx_chunk_root_current ON knowledge.tb_knowledge_chunk(root_chunk_id, is_current) WHERE deleted = false;
|
||||
CREATE INDEX idx_chunk_current ON knowledge.tb_knowledge_chunk(is_current) WHERE deleted = false AND is_current = true;
|
||||
-- 向量检索索引(需要安装pgvector扩展,建议只索引当前版本)
|
||||
-- CREATE INDEX idx_chunk_embedding ON knowledge.tb_knowledge_chunk USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100) WHERE deleted = false AND is_current = true;
|
||||
|
||||
COMMENT ON TABLE knowledge.tb_knowledge_chunk IS '知识文档片段表(RAG检索)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.version IS '分段版本号(用于乐观锁,防止并发编辑冲突)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.embedding IS '向量嵌入(需要pgvector扩展)';
|
||||
COMMENT ON TABLE knowledge.tb_knowledge_chunk IS '知识文档片段表(RAG检索基本单位,支持chunk级版本控制)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.version IS 'chunk版本号(整数,每次修改自动+1)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.root_chunk_id IS '根chunk ID(同一chunk的所有版本共享此ID,首次创建时等于chunk_id)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.is_current IS '是否当前使用的版本(每个root_chunk_id只有一个is_current=true,RAG检索时只使用当前版本)';
|
||||
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.embedding IS '向量嵌入(需要pgvector扩展,建议只为is_current=true的chunk生成)';
|
||||
|
||||
-- 知识访问日志表
|
||||
DROP TABLE IF EXISTS knowledge.tb_knowledge_access_log CASCADE;
|
||||
|
||||
@@ -118,7 +118,7 @@ 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_code VARCHAR(50) NOT NULL, -- 渠道编码:app/sms/email/wechat/dingtalk等
|
||||
channel_name VARCHAR(100) NOT NULL, -- 渠道名称
|
||||
channel_desc VARCHAR(255) DEFAULT NULL, -- 渠道描述
|
||||
config JSON DEFAULT NULL, -- 渠道配置(如API密钥、服务器地址等)
|
||||
|
||||
@@ -10,7 +10,8 @@ CREATE TABLE sys.tb_sys_user (
|
||||
usercode VARCHAR(100) DEFAULT NULL, -- 用户code。sso同步数据获取
|
||||
password VARCHAR(128) NOT NULL, -- 密码(建议存储 bcrypt/argon2 哈希)
|
||||
email VARCHAR(100), -- 电子邮件
|
||||
phone VARCHAR(20), -- 电话号码
|
||||
phone VARCHAR(500), -- 电话号码
|
||||
phone_hash VARCHAR(200), -- 电话hash
|
||||
wechat_id VARCHAR(50), -- 微信ID
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间(使用带时区时间)
|
||||
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
-- =============================
|
||||
-- 智能客服系统业务模块
|
||||
-- 智能客服系统业务模块(工单系统)
|
||||
-- 支持:微信小程序客户咨询、智能问答、工单管理、CRM集成
|
||||
-- =============================
|
||||
CREATE SCHEMA IF NOT EXISTS customer_service;
|
||||
CREATE SCHEMA IF NOT EXISTS workcase;
|
||||
|
||||
-- 客户信息表
|
||||
DROP TABLE IF EXISTS customer_service.tb_customer CASCADE;
|
||||
CREATE TABLE customer_service.tb_customer (
|
||||
DROP TABLE IF EXISTS workcase.tb_customer CASCADE;
|
||||
CREATE TABLE workcase.tb_customer (
|
||||
optsn VARCHAR(50) NOT NULL, -- 流水号
|
||||
customer_id VARCHAR(50) NOT NULL, -- 客户ID
|
||||
customer_no VARCHAR(100), -- 客户编号
|
||||
@@ -45,16 +45,16 @@ CREATE TABLE customer_service.tb_customer (
|
||||
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;
|
||||
CREATE INDEX idx_customer_type ON workcase.tb_customer(customer_type) WHERE deleted = false;
|
||||
CREATE INDEX idx_customer_level ON workcase.tb_customer(customer_level) WHERE deleted = false;
|
||||
CREATE INDEX idx_customer_wechat ON workcase.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';
|
||||
COMMENT ON TABLE workcase.tb_customer IS '客户信息表';
|
||||
COMMENT ON COLUMN workcase.tb_customer.customer_level IS '客户等级:vip/important/normal/potential';
|
||||
|
||||
-- 会话表
|
||||
DROP TABLE IF EXISTS customer_service.tb_conversation CASCADE;
|
||||
CREATE TABLE customer_service.tb_conversation (
|
||||
DROP TABLE IF EXISTS workcase.tb_conversation CASCADE;
|
||||
CREATE TABLE workcase.tb_conversation (
|
||||
optsn VARCHAR(50) NOT NULL, -- 流水号
|
||||
conversation_id VARCHAR(50) NOT NULL, -- 会话ID
|
||||
customer_id VARCHAR(50) NOT NULL, -- 客户ID
|
||||
@@ -81,19 +81,19 @@ CREATE TABLE customer_service.tb_conversation (
|
||||
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
|
||||
PRIMARY KEY (conversation_id),
|
||||
UNIQUE (optsn),
|
||||
FOREIGN KEY (customer_id) REFERENCES customer_service.tb_customer(customer_id)
|
||||
FOREIGN KEY (customer_id) REFERENCES workcase.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;
|
||||
CREATE INDEX idx_conv_customer ON workcase.tb_conversation(customer_id, session_start_time DESC) WHERE deleted = false;
|
||||
CREATE INDEX idx_conv_status ON workcase.tb_conversation(conversation_status) WHERE deleted = false;
|
||||
CREATE INDEX idx_conv_agent ON workcase.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';
|
||||
COMMENT ON TABLE workcase.tb_conversation IS '会话表';
|
||||
COMMENT ON COLUMN workcase.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 (
|
||||
DROP TABLE IF EXISTS workcase.tb_conversation_message CASCADE;
|
||||
CREATE TABLE workcase.tb_conversation_message (
|
||||
optsn VARCHAR(50) NOT NULL, -- 流水号
|
||||
message_id VARCHAR(50) NOT NULL, -- 消息ID
|
||||
conversation_id VARCHAR(50) NOT NULL, -- 所属会话ID
|
||||
@@ -117,18 +117,18 @@ CREATE TABLE customer_service.tb_conversation_message (
|
||||
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
|
||||
PRIMARY KEY (message_id),
|
||||
UNIQUE (optsn),
|
||||
FOREIGN KEY (conversation_id) REFERENCES customer_service.tb_conversation(conversation_id)
|
||||
FOREIGN KEY (conversation_id) REFERENCES workcase.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;
|
||||
CREATE INDEX idx_msg_conversation ON workcase.tb_conversation_message(conversation_id, create_time) WHERE deleted = false;
|
||||
CREATE INDEX idx_msg_sender ON workcase.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';
|
||||
COMMENT ON TABLE workcase.tb_conversation_message IS '会话消息表';
|
||||
COMMENT ON COLUMN workcase.tb_conversation_message.sentiment IS '情感分析:positive/neutral/negative';
|
||||
|
||||
-- 工单表
|
||||
DROP TABLE IF EXISTS customer_service.tb_ticket CASCADE;
|
||||
CREATE TABLE customer_service.tb_ticket (
|
||||
DROP TABLE IF EXISTS workcase.tb_ticket CASCADE;
|
||||
CREATE TABLE workcase.tb_ticket (
|
||||
optsn VARCHAR(50) NOT NULL, -- 流水号
|
||||
ticket_id VARCHAR(50) NOT NULL, -- 工单ID
|
||||
ticket_no VARCHAR(100) NOT NULL, -- 工单编号
|
||||
@@ -166,21 +166,21 @@ CREATE TABLE customer_service.tb_ticket (
|
||||
PRIMARY KEY (ticket_id),
|
||||
UNIQUE (optsn),
|
||||
UNIQUE (ticket_no),
|
||||
FOREIGN KEY (customer_id) REFERENCES customer_service.tb_customer(customer_id)
|
||||
FOREIGN KEY (customer_id) REFERENCES workcase.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;
|
||||
CREATE INDEX idx_ticket_customer ON workcase.tb_ticket(customer_id) WHERE deleted = false;
|
||||
CREATE INDEX idx_ticket_status ON workcase.tb_ticket(ticket_status) WHERE deleted = false;
|
||||
CREATE INDEX idx_ticket_assigned ON workcase.tb_ticket(assigned_to) WHERE deleted = false;
|
||||
CREATE INDEX idx_ticket_priority ON workcase.tb_ticket(priority) WHERE deleted = false;
|
||||
CREATE INDEX idx_ticket_sla ON workcase.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';
|
||||
COMMENT ON TABLE workcase.tb_ticket IS '工单表';
|
||||
COMMENT ON COLUMN workcase.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 (
|
||||
DROP TABLE IF EXISTS workcase.tb_ticket_log CASCADE;
|
||||
CREATE TABLE workcase.tb_ticket_log (
|
||||
optsn VARCHAR(50) NOT NULL, -- 流水号
|
||||
log_id VARCHAR(50) NOT NULL, -- 日志ID
|
||||
ticket_id VARCHAR(50) NOT NULL, -- 工单ID
|
||||
@@ -195,16 +195,16 @@ CREATE TABLE customer_service.tb_ticket_log (
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||||
PRIMARY KEY (log_id),
|
||||
UNIQUE (optsn),
|
||||
FOREIGN KEY (ticket_id) REFERENCES customer_service.tb_ticket(ticket_id)
|
||||
FOREIGN KEY (ticket_id) REFERENCES workcase.tb_ticket(ticket_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ticket_log_ticket ON customer_service.tb_ticket_log(ticket_id, create_time DESC);
|
||||
CREATE INDEX idx_ticket_log_ticket ON workcase.tb_ticket_log(ticket_id, create_time DESC);
|
||||
|
||||
COMMENT ON TABLE customer_service.tb_ticket_log IS '工单处理记录表';
|
||||
COMMENT ON TABLE workcase.tb_ticket_log IS '工单处理记录表';
|
||||
|
||||
-- FAQ表(常见问题)
|
||||
DROP TABLE IF EXISTS customer_service.tb_faq CASCADE;
|
||||
CREATE TABLE customer_service.tb_faq (
|
||||
DROP TABLE IF EXISTS workcase.tb_faq CASCADE;
|
||||
CREATE TABLE workcase.tb_faq (
|
||||
optsn VARCHAR(50) NOT NULL, -- 流水号
|
||||
faq_id VARCHAR(50) NOT NULL, -- FAQ ID
|
||||
knowledge_id VARCHAR(50), -- 关联知识库ID
|
||||
@@ -229,14 +229,14 @@ CREATE TABLE customer_service.tb_faq (
|
||||
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;
|
||||
CREATE INDEX idx_faq_category ON workcase.tb_faq(category) WHERE deleted = false;
|
||||
CREATE INDEX idx_faq_published ON workcase.tb_faq(is_published) WHERE deleted = false AND is_published = true;
|
||||
|
||||
COMMENT ON TABLE customer_service.tb_faq IS 'FAQ常见问题表';
|
||||
COMMENT ON TABLE workcase.tb_faq IS 'FAQ常见问题表';
|
||||
|
||||
-- 客服评价表
|
||||
DROP TABLE IF EXISTS customer_service.tb_service_evaluation CASCADE;
|
||||
CREATE TABLE customer_service.tb_service_evaluation (
|
||||
DROP TABLE IF EXISTS workcase.tb_service_evaluation CASCADE;
|
||||
CREATE TABLE workcase.tb_service_evaluation (
|
||||
optsn VARCHAR(50) NOT NULL, -- 流水号
|
||||
evaluation_id VARCHAR(50) NOT NULL, -- 评价ID
|
||||
customer_id VARCHAR(50) NOT NULL, -- 客户ID
|
||||
@@ -253,17 +253,17 @@ CREATE TABLE customer_service.tb_service_evaluation (
|
||||
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
|
||||
PRIMARY KEY (evaluation_id),
|
||||
UNIQUE (optsn),
|
||||
FOREIGN KEY (customer_id) REFERENCES customer_service.tb_customer(customer_id)
|
||||
FOREIGN KEY (customer_id) REFERENCES workcase.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;
|
||||
CREATE INDEX idx_eval_customer ON workcase.tb_service_evaluation(customer_id) WHERE deleted = false;
|
||||
CREATE INDEX idx_eval_rating ON workcase.tb_service_evaluation(rating) WHERE deleted = false;
|
||||
|
||||
COMMENT ON TABLE customer_service.tb_service_evaluation IS '客服评价表';
|
||||
COMMENT ON TABLE workcase.tb_service_evaluation IS '客服评价表';
|
||||
|
||||
-- CRM集成配置表
|
||||
DROP TABLE IF EXISTS customer_service.tb_crm_config CASCADE;
|
||||
CREATE TABLE customer_service.tb_crm_config (
|
||||
DROP TABLE IF EXISTS workcase.tb_crm_config CASCADE;
|
||||
CREATE TABLE workcase.tb_crm_config (
|
||||
optsn VARCHAR(50) NOT NULL, -- 流水号
|
||||
config_id VARCHAR(50) NOT NULL, -- 配置ID
|
||||
crm_system VARCHAR(50) NOT NULL, -- CRM系统名称
|
||||
@@ -286,4 +286,4 @@ CREATE TABLE customer_service.tb_crm_config (
|
||||
UNIQUE (optsn)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE customer_service.tb_crm_config IS 'CRM集成配置表';
|
||||
COMMENT ON TABLE workcase.tb_crm_config IS 'CRM集成配置表';
|
||||
@@ -28,10 +28,23 @@ INSERT INTO config.tb_sys_config (
|
||||
('CFG-0303', 'cfg_storage_base', 'storage.basePath', '存储路径', '/data/urban-lifeline', 'String', 'input', '本地存储基路径', NULL, NULL, 'storage', 'mod_file', 30, 0, '当 backend=local', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
|
||||
-- 通知(邮件/SMS)
|
||||
('CFG-0401', 'cfg_mail_host', 'mail.smtp.host', 'SMTP主机', '', 'String', 'input', 'SMTP主机', NULL, NULL, 'notify', 'mod_message', 10, 1, '留空为未配置', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0402', 'cfg_mail_port', 'mail.smtp.port', 'SMTP端口', '465', 'INTEGER', 'input', 'SMTP端口', NULL, NULL, 'notify', 'mod_message', 20, 1, 'SSL常用465', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0403', 'cfg_mail_from', 'mail.from', '发件人邮箱', '', 'String', 'input', '发件人邮箱', NULL, NULL, 'notify', 'mod_message', 30, 1, '如 no-reply@x.com', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0411', 'cfg_sms_provider', 'sms.provider', '短信服务商', '', 'String', 'select', '短信服务商', NULL, '["aliyun", "tencent"]'::json, 'notify', 'mod_message', 40, 1, '如 aliyun/tencent', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
-- 邮件配置
|
||||
('CFG-0401', 'cfg_mail_host', 'email.host', 'SMTP服务器地址', 'smtp.qq.com', 'String', 'input', 'SMTP服务器地址', NULL, NULL, 'notify', 'mod_message', 10, 1, '邮件发送服务器地址', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0402', 'cfg_mail_port', 'email.port', 'SMTP端口', '587', 'INTEGER', 'input', 'SMTP服务器端口', NULL, NULL, 'notify', 'mod_message', 20, 1, '常用:25/465/587', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0403', 'cfg_mail_username', 'email.username', '发件人邮箱', '3223905473@qq.com', 'String', 'input', '发件人邮箱地址', NULL, NULL, 'notify', 'mod_message', 30, 1, '用于发送邮件的邮箱账号', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0404', 'cfg_mail_password', 'email.password', '邮箱授权码', 'xmdmxvtjumxocicc', 'String', 'password', '邮箱授权码/密码', NULL, NULL, 'notify', 'mod_message', 40, 1, '邮箱的授权码或密码', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0405', 'cfg_mail_fromname', 'email.fromName', '发件人名称', 'urban-lifeline平台', 'String', 'input', '发件人显示名称', NULL, NULL, 'notify', 'mod_message', 50, 1, '邮件中显示的发件人名称', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0406', 'cfg_mail_ssl', 'email.ssl.enable', '启用SSL', 'true', 'BOOLEAN', 'switch', '是否启用SSL', NULL, NULL, 'notify', 'mod_message', 60, 1, 'SSL加密连接', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0407', 'cfg_mail_timeout', 'email.timeout', '连接超时时间', '30000', 'INTEGER', 'input', '连接超时时间(毫秒)', NULL, NULL, 'notify', 'mod_message', 70, 1, 'SMTP连接超时时间(5000-60000)', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
|
||||
-- 短信配置
|
||||
('CFG-0411', 'cfg_sms_provider', 'sms.provider', '短信服务商', 'aliyun', 'String', 'select', '短信服务提供商', NULL, '["aliyun", "tencent"]'::json, 'notify', 'mod_message', 80, 1, '短信服务提供商类型', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0412', 'cfg_sms_keyid', 'sms.accessKeyId', 'AccessKey ID', 'LTAI5t68do3qVXx5Rufugt3X', 'String', 'input', '短信服务AccessKey ID', NULL, NULL, 'notify', 'mod_message', 90, 1, '云服务商的AccessKey ID', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0413', 'cfg_sms_secret', 'sms.accessKeySecret', 'AccessKey Secret', '2vD9ToIff49Vph4JQXsn0Cy8nXQfzA', 'String', 'password', '短信服务AccessKey Secret', NULL, NULL, 'notify', 'mod_message', 100, 1, '云服务商的AccessKey Secret', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0414', 'cfg_sms_sign', 'sms.signName', '短信签名', 'urban-lifeline', 'String', 'input', '短信签名', NULL, NULL, 'notify', 'mod_message', 110, 1, '发送短信使用的签名', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0415', 'cfg_sms_tpl_login', 'sms.templateCode.login', '登录验证码模板', 'SMS_491985030', 'String', 'input', '登录验证码模板编码', NULL, NULL, 'notify', 'mod_message', 120, 1, '登录验证码短信模板', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0416', 'cfg_sms_tpl_register', 'sms.templateCode.register','注册验证码模板', 'SMS_491985030', 'String', 'input', '注册验证码模板编码', NULL, NULL, 'notify', 'mod_message', 130, 1, '注册验证码短信模板', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
('CFG-0417', 'cfg_sms_timeout', 'sms.timeout', '请求超时时间', '30000', 'INTEGER', 'input', '请求超时时间(毫秒)', NULL, NULL, 'notify', 'mod_message', 140, 1, 'API请求超时时间(5000-60000)', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
|
||||
-- 日志与审计
|
||||
('CFG-0501', 'cfg_log_level', 'log.level', '日志级别', 'INFO', 'String', 'select', '系统日志级别', NULL, '["DEBUG", "INFO", "WARN", "ERROR"]'::json, 'log', 'mod_system', 10, 0, 'DEBUG/INFO/WARN/ERROR', 'system', NULL, NULL, now(), NULL, NULL, false),
|
||||
|
||||
1
urbanLifelineServ/.pids/auth.pid
Normal file
1
urbanLifelineServ/.pids/auth.pid
Normal file
@@ -0,0 +1 @@
|
||||
1028
|
||||
1
urbanLifelineServ/.pids/gateway.pid
Normal file
1
urbanLifelineServ/.pids/gateway.pid
Normal file
@@ -0,0 +1 @@
|
||||
1003
|
||||
1
urbanLifelineServ/.pids/system.pid
Normal file
1
urbanLifelineServ/.pids/system.pid
Normal file
@@ -0,0 +1 @@
|
||||
1053
|
||||
163
urbanLifelineServ/.vscode/launch.json
vendored
163
urbanLifelineServ/.vscode/launch.json
vendored
@@ -3,21 +3,162 @@
|
||||
"configurations": [
|
||||
{
|
||||
"type": "java",
|
||||
"name": "SystemApp",
|
||||
"name": "Gateway (8180)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.SystemApp",
|
||||
"mainClass": "org.xyzh.gateway.GatewayApplication",
|
||||
"projectName": "gateway",
|
||||
"cwd": "${workspaceFolder}/gateway",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8180"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Auth (8181)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.auth.AuthApp",
|
||||
"projectName": "auth",
|
||||
"cwd": "${workspaceFolder}/auth",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8181"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "System (8182)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.system.SystemApp",
|
||||
"projectName": "system",
|
||||
"cwd": "${workspaceFolder}/system",
|
||||
"args": [
|
||||
"--spring.profiles.active=dev"
|
||||
],
|
||||
"vmArgs": [
|
||||
"-Dspring.profiles.active=dev"
|
||||
],
|
||||
"env": {
|
||||
"SPRING_PROFILES_ACTIVE": "dev"
|
||||
},
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8182"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Log (8183)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.log.LogApp",
|
||||
"projectName": "log",
|
||||
"cwd": "${workspaceFolder}/log",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8183"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "File (8184)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.file.FileApp",
|
||||
"projectName": "file",
|
||||
"cwd": "${workspaceFolder}/file",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8184"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Message (8185)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.message.MessageApp",
|
||||
"projectName": "message",
|
||||
"cwd": "${workspaceFolder}/message",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8185"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Bidding (8186)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.bidding.BiddingApp",
|
||||
"projectName": "bidding",
|
||||
"cwd": "${workspaceFolder}/bidding",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8186"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Platform (8187)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.platform.PlatformApp",
|
||||
"projectName": "platform",
|
||||
"cwd": "${workspaceFolder}/platform",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8187"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Workcase (8188)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.workcase.WorkcaseApp",
|
||||
"projectName": "workcase",
|
||||
"cwd": "${workspaceFolder}/workcase",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8188"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Crontab (8189)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.crontab.CrontabApp",
|
||||
"projectName": "crontab",
|
||||
"cwd": "${workspaceFolder}/crontab",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8189"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Agent (8190)",
|
||||
"request": "launch",
|
||||
"mainClass": "org.xyzh.agent.AgentApp",
|
||||
"projectName": "agent",
|
||||
"cwd": "${workspaceFolder}/agent",
|
||||
"args": ["--spring.profiles.active=dev"],
|
||||
"vmArgs": ["-Dserver.port=8190"],
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Core Services (Gateway + Auth + System)",
|
||||
"configurations": [
|
||||
"Gateway (8180)",
|
||||
"Auth (8181)",
|
||||
"System (8182)"
|
||||
],
|
||||
"stopAll": true,
|
||||
"presentation": {
|
||||
"hidden": false,
|
||||
"group": "服务组合",
|
||||
"order": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "All Services",
|
||||
"configurations": [
|
||||
"Gateway (8180)",
|
||||
"Auth (8181)",
|
||||
"System (8182)",
|
||||
"Log (8183)",
|
||||
"File (8184)",
|
||||
"Message (8185)",
|
||||
"Bidding (8186)",
|
||||
"Platform (8187)",
|
||||
"Workcase (8188)",
|
||||
"Crontab (8189)",
|
||||
"Agent (8190)"
|
||||
],
|
||||
"stopAll": true,
|
||||
"presentation": {
|
||||
"hidden": false,
|
||||
"group": "服务组合",
|
||||
"order": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
23
urbanLifelineServ/.vscode/settings.json
vendored
23
urbanLifelineServ/.vscode/settings.json
vendored
@@ -4,12 +4,31 @@
|
||||
"maven.view": "hierarchical",
|
||||
"tabSize": 4,
|
||||
"java.debug.settings.onBuildFailureProceed": true,
|
||||
"java.configuration.maven.userSettings": null,
|
||||
"java.configuration.maven.userSettings": "",
|
||||
"java.import.maven.enabled": true,
|
||||
"java.project.referencedLibraries": [
|
||||
"**/*.jar"
|
||||
],
|
||||
"maven.terminal.useJavaHome": true,
|
||||
"java.configuration.runtimes": [],
|
||||
"Codegeex.RepoIndex": true
|
||||
"Codegeex.RepoIndex": true,
|
||||
"terminal.integrated.defaultProfile.windows": "Command Prompt",
|
||||
"terminal.integrated.profiles.windows": {
|
||||
"PowerShell": {
|
||||
"source": "PowerShell",
|
||||
"args": ["-NoExit", "-Command", "chcp 65001"]
|
||||
},
|
||||
"Command Prompt": {
|
||||
"path": "cmd.exe",
|
||||
"args": ["/k", "chcp 65001"]
|
||||
},
|
||||
"Git Bash": {
|
||||
"path": "F:\\Environment\\Git\\bin\\bash.exe",
|
||||
"args": ["-i"],
|
||||
"env": {
|
||||
"LANG": "zh_CN.UTF-8",
|
||||
"LC_ALL": "zh_CN.UTF-8"
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
247
urbanLifelineServ/Makefile
Normal file
247
urbanLifelineServ/Makefile
Normal file
@@ -0,0 +1,247 @@
|
||||
# ================================================
|
||||
# Urban Lifeline 微服务管理 Makefile
|
||||
# ================================================
|
||||
|
||||
# 项目根目录
|
||||
PROJECT_ROOT := $(shell pwd)
|
||||
|
||||
# Maven 命令
|
||||
MAVEN := mvn
|
||||
MAVEN_OPTS := -Dmaven.test.skip=true
|
||||
|
||||
# 服务列表
|
||||
SERVICES := gateway auth system log file message bidding platform workcase crontab agent
|
||||
|
||||
# 服务端口映射
|
||||
PORT_gateway := 8080
|
||||
PORT_auth := 8081
|
||||
PORT_system := 8082
|
||||
PORT_log := 8083
|
||||
PORT_file := 8084
|
||||
PORT_message := 8085
|
||||
PORT_bidding := 8086
|
||||
PORT_platform := 8087
|
||||
PORT_workcase := 8088
|
||||
PORT_crontab := 8089
|
||||
PORT_agent := 8090
|
||||
|
||||
# 服务主类映射
|
||||
MAINCLASS_gateway := org.xyzh.gateway.GatewayApplication
|
||||
MAINCLASS_auth := org.xyzh.auth.AuthApp
|
||||
MAINCLASS_system := org.xyzh.system.SystemApp
|
||||
MAINCLASS_log := org.xyzh.log.LogApp
|
||||
MAINCLASS_file := org.xyzh.file.FileApp
|
||||
MAINCLASS_message := org.xyzh.message.MessageApp
|
||||
MAINCLASS_bidding := org.xyzh.bidding.BiddingApp
|
||||
MAINCLASS_platform := org.xyzh.platform.PlatformApp
|
||||
MAINCLASS_workcase := org.xyzh.workcase.WorkcaseApp
|
||||
MAINCLASS_crontab := org.xyzh.crontab.CrontabApp
|
||||
MAINCLASS_agent := org.xyzh.agent.AgentApp
|
||||
|
||||
# PID 文件目录
|
||||
PID_DIR := $(PROJECT_ROOT)/.pids
|
||||
LOG_DIR := $(PROJECT_ROOT)/.logs
|
||||
|
||||
# 颜色定义
|
||||
COLOR_RESET := \033[0m
|
||||
COLOR_GREEN := \033[0;32m
|
||||
COLOR_YELLOW := \033[0;33m
|
||||
COLOR_BLUE := \033[0;34m
|
||||
COLOR_RED := \033[0;31m
|
||||
|
||||
# ================================================
|
||||
# 帮助信息
|
||||
# ================================================
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "$(COLOR_BLUE)=============================================$(COLOR_RESET)"
|
||||
@echo "$(COLOR_BLUE) Urban Lifeline 微服务管理工具$(COLOR_RESET)"
|
||||
@echo "$(COLOR_BLUE)=============================================$(COLOR_RESET)"
|
||||
@echo ""
|
||||
@echo "$(COLOR_GREEN)构建命令:$(COLOR_RESET)"
|
||||
@echo " make build - 构建所有服务"
|
||||
@echo " make build-<service> - 构建指定服务 (如: make build-system)"
|
||||
@echo " make clean - 清理所有服务"
|
||||
@echo ""
|
||||
@echo "$(COLOR_GREEN)启动命令:$(COLOR_RESET)"
|
||||
@echo " make start - 启动所有服务"
|
||||
@echo " make start-<service> - 启动指定服务 (如: make start-system)"
|
||||
@echo " make start-core - 启动核心服务 (gateway, auth, system)"
|
||||
@echo ""
|
||||
@echo "$(COLOR_GREEN)停止命令:$(COLOR_RESET)"
|
||||
@echo " make stop - 停止所有服务"
|
||||
@echo " make stop-<service> - 停止指定服务 (如: make stop-system)"
|
||||
@echo ""
|
||||
@echo "$(COLOR_GREEN)重启命令:$(COLOR_RESET)"
|
||||
@echo " make restart - 重启所有服务"
|
||||
@echo " make restart-<service> - 重启指定服务 (如: make restart-system)"
|
||||
@echo ""
|
||||
@echo "$(COLOR_GREEN)状态查看:$(COLOR_RESET)"
|
||||
@echo " make status - 查看所有服务状态"
|
||||
@echo " make status-<service> - 查看指定服务状态"
|
||||
@echo " make logs-<service> - 查看指定服务日志"
|
||||
@echo ""
|
||||
@echo "$(COLOR_GREEN)可用服务:$(COLOR_RESET)"
|
||||
@echo " $(SERVICES)"
|
||||
@echo ""
|
||||
|
||||
# ================================================
|
||||
# 初始化目录
|
||||
# ================================================
|
||||
.PHONY: init-dirs
|
||||
init-dirs:
|
||||
@mkdir -p $(PID_DIR)
|
||||
@mkdir -p $(LOG_DIR)
|
||||
|
||||
# ================================================
|
||||
# 构建相关
|
||||
# ================================================
|
||||
.PHONY: build
|
||||
build: init-dirs
|
||||
@echo "$(COLOR_YELLOW)开始构建所有服务...$(COLOR_RESET)"
|
||||
@$(MAVEN) clean install $(MAVEN_OPTS)
|
||||
@echo "$(COLOR_GREEN)✓ 所有服务构建完成$(COLOR_RESET)"
|
||||
|
||||
.PHONY: $(addprefix build-,$(SERVICES))
|
||||
$(addprefix build-,$(SERVICES)): build-%:
|
||||
@echo "$(COLOR_YELLOW)开始构建 $* 服务...$(COLOR_RESET)"
|
||||
@cd $(PROJECT_ROOT)/$* && $(MAVEN) clean install $(MAVEN_OPTS)
|
||||
@echo "$(COLOR_GREEN)✓ $* 服务构建完成$(COLOR_RESET)"
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@echo "$(COLOR_YELLOW)开始清理所有服务...$(COLOR_RESET)"
|
||||
@$(MAVEN) clean
|
||||
@rm -rf $(PID_DIR)
|
||||
@rm -rf $(LOG_DIR)
|
||||
@echo "$(COLOR_GREEN)✓ 清理完成$(COLOR_RESET)"
|
||||
|
||||
# ================================================
|
||||
# 启动服务
|
||||
# ================================================
|
||||
.PHONY: start
|
||||
start: init-dirs $(addprefix start-,$(SERVICES))
|
||||
@echo "$(COLOR_GREEN)✓ 所有服务启动完成$(COLOR_RESET)"
|
||||
|
||||
.PHONY: start-core
|
||||
start-core: init-dirs start-gateway start-auth start-system
|
||||
@echo "$(COLOR_GREEN)✓ 核心服务启动完成$(COLOR_RESET)"
|
||||
|
||||
.PHONY: $(addprefix start-,$(SERVICES))
|
||||
$(addprefix start-,$(SERVICES)): start-%:
|
||||
@if [ -f "$(PID_DIR)/$*.pid" ] && kill -0 $$(cat $(PID_DIR)/$*.pid) 2>/dev/null; then \
|
||||
echo "$(COLOR_YELLOW)⚠ $* 服务已在运行中 (PID: $$(cat $(PID_DIR)/$*.pid))$(COLOR_RESET)"; \
|
||||
else \
|
||||
echo "$(COLOR_BLUE)启动 $* 服务...$(COLOR_RESET)"; \
|
||||
nohup $(MAVEN) -pl $* spring-boot:run > $(LOG_DIR)/$*.log 2>&1 & \
|
||||
echo $$! > $(PID_DIR)/$*.pid; \
|
||||
sleep 2; \
|
||||
if kill -0 $$(cat $(PID_DIR)/$*.pid) 2>/dev/null; then \
|
||||
echo "$(COLOR_GREEN)✓ $* 服务启动成功 (PID: $$(cat $(PID_DIR)/$*.pid), Port: $(PORT_$*))$(COLOR_RESET)"; \
|
||||
else \
|
||||
echo "$(COLOR_RED)✗ $* 服务启动失败$(COLOR_RESET)"; \
|
||||
rm -f $(PID_DIR)/$*.pid; \
|
||||
fi; \
|
||||
fi
|
||||
|
||||
# ================================================
|
||||
# 停止服务
|
||||
# ================================================
|
||||
.PHONY: stop
|
||||
stop: $(addprefix stop-,$(SERVICES))
|
||||
@echo "$(COLOR_GREEN)✓ 所有服务停止完成$(COLOR_RESET)"
|
||||
|
||||
.PHONY: $(addprefix stop-,$(SERVICES))
|
||||
$(addprefix stop-,$(SERVICES)): stop-%:
|
||||
@if [ -f "$(PID_DIR)/$*.pid" ]; then \
|
||||
PID=$$(cat $(PID_DIR)/$*.pid); \
|
||||
if kill -0 $$PID 2>/dev/null; then \
|
||||
echo "$(COLOR_YELLOW)停止 $* 服务 (PID: $$PID)...$(COLOR_RESET)"; \
|
||||
kill $$PID; \
|
||||
sleep 2; \
|
||||
if kill -0 $$PID 2>/dev/null; then \
|
||||
echo "$(COLOR_RED)强制停止 $* 服务...$(COLOR_RESET)"; \
|
||||
kill -9 $$PID; \
|
||||
fi; \
|
||||
rm -f $(PID_DIR)/$*.pid; \
|
||||
echo "$(COLOR_GREEN)✓ $* 服务已停止$(COLOR_RESET)"; \
|
||||
else \
|
||||
echo "$(COLOR_YELLOW)⚠ $* 服务未运行$(COLOR_RESET)"; \
|
||||
rm -f $(PID_DIR)/$*.pid; \
|
||||
fi; \
|
||||
else \
|
||||
echo "$(COLOR_YELLOW)⚠ $* 服务未运行$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
# ================================================
|
||||
# 重启服务
|
||||
# ================================================
|
||||
.PHONY: restart
|
||||
restart: stop start
|
||||
@echo "$(COLOR_GREEN)✓ 所有服务重启完成$(COLOR_RESET)"
|
||||
|
||||
.PHONY: $(addprefix restart-,$(SERVICES))
|
||||
$(addprefix restart-,$(SERVICES)): restart-%: stop-% start-%
|
||||
@echo "$(COLOR_GREEN)✓ $* 服务重启完成$(COLOR_RESET)"
|
||||
|
||||
# ================================================
|
||||
# 查看状态
|
||||
# ================================================
|
||||
.PHONY: status
|
||||
status:
|
||||
@echo "$(COLOR_BLUE)=============================================$(COLOR_RESET)"
|
||||
@echo "$(COLOR_BLUE) 服务运行状态$(COLOR_RESET)"
|
||||
@echo "$(COLOR_BLUE)=============================================$(COLOR_RESET)"
|
||||
@for service in $(SERVICES); do \
|
||||
printf "%-15s" "$$service:"; \
|
||||
if [ -f "$(PID_DIR)/$$service.pid" ]; then \
|
||||
PID=$$(cat $(PID_DIR)/$$service.pid); \
|
||||
if kill -0 $$PID 2>/dev/null; then \
|
||||
echo "$(COLOR_GREEN)✓ Running (PID: $$PID)$(COLOR_RESET)"; \
|
||||
else \
|
||||
echo "$(COLOR_RED)✗ Stopped$(COLOR_RESET)"; \
|
||||
rm -f $(PID_DIR)/$$service.pid; \
|
||||
fi; \
|
||||
else \
|
||||
echo "$(COLOR_YELLOW)○ Not Started$(COLOR_RESET)"; \
|
||||
fi; \
|
||||
done
|
||||
@echo ""
|
||||
|
||||
.PHONY: $(addprefix status-,$(SERVICES))
|
||||
$(addprefix status-,$(SERVICES)): status-%:
|
||||
@if [ -f "$(PID_DIR)/$*.pid" ]; then \
|
||||
PID=$$(cat $(PID_DIR)/$*.pid); \
|
||||
if kill -0 $$PID 2>/dev/null; then \
|
||||
echo "$(COLOR_GREEN)✓ $* 服务运行中 (PID: $$PID, Port: $(PORT_$*))$(COLOR_RESET)"; \
|
||||
else \
|
||||
echo "$(COLOR_RED)✗ $* 服务已停止$(COLOR_RESET)"; \
|
||||
fi; \
|
||||
else \
|
||||
echo "$(COLOR_YELLOW)○ $* 服务未启动$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
# ================================================
|
||||
# 查看日志
|
||||
# ================================================
|
||||
.PHONY: $(addprefix logs-,$(SERVICES))
|
||||
$(addprefix logs-,$(SERVICES)): logs-%:
|
||||
@if [ -f "$(LOG_DIR)/$*.log" ]; then \
|
||||
tail -f $(LOG_DIR)/$*.log; \
|
||||
else \
|
||||
echo "$(COLOR_YELLOW)⚠ $* 服务日志文件不存在$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
# ================================================
|
||||
# 快捷命令
|
||||
# ================================================
|
||||
.PHONY: dev
|
||||
dev: start-core
|
||||
@echo "$(COLOR_GREEN)✓ 开发环境启动完成 (gateway, auth, system)$(COLOR_RESET)"
|
||||
|
||||
.PHONY: prod
|
||||
prod: start
|
||||
@echo "$(COLOR_GREEN)✓ 生产环境启动完成$(COLOR_RESET)"
|
||||
|
||||
# 默认目标
|
||||
.DEFAULT_GOAL := help
|
||||
21
urbanLifelineServ/agent/pom.xml
Normal file
21
urbanLifelineServ/agent/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>urban-lifeline</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>agent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.xyzh.agent;
|
||||
|
||||
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.agent", // 当前agent模块
|
||||
"org.xyzh.common" // 公共模块
|
||||
})
|
||||
public class AgentApp {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AgentApp.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
logger.info("======================== AgentApp 启动中 =========================");
|
||||
SpringApplication.run(AgentApp.class, args);
|
||||
logger.info("======================== AgentApp 启动成功 =========================");
|
||||
}
|
||||
}
|
||||
89
urbanLifelineServ/agent/src/main/resources/application.yml
Normal file
89
urbanLifelineServ/agent/src/main/resources/application.yml
Normal file
@@ -0,0 +1,89 @@
|
||||
# ================== Server ==================
|
||||
server:
|
||||
port: 8190
|
||||
servlet:
|
||||
context-path: /urban-lifeline/agent
|
||||
|
||||
# ================== Auth ====================
|
||||
urban-lifeline:
|
||||
auth:
|
||||
enabled: true
|
||||
whitelist:
|
||||
- /swagger-ui/**
|
||||
- /swagger-ui.html
|
||||
- /v3/api-docs/**
|
||||
- /webjars/**
|
||||
- /favicon.ico
|
||||
- /error
|
||||
- /actuator/health
|
||||
- /actuator/info
|
||||
|
||||
security:
|
||||
aes:
|
||||
secret-key: 1234567890qwer
|
||||
|
||||
# ================== Spring ==================
|
||||
spring:
|
||||
application:
|
||||
name: agent-service
|
||||
|
||||
# ================== Spring Cloud Nacos ==================
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
namespace: dev
|
||||
group: DEFAULT_GROUP
|
||||
|
||||
# ================== DataSource ==================
|
||||
datasource:
|
||||
url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline
|
||||
username: postgres
|
||||
password: postgres
|
||||
driver-class-name: org.postgresql.Driver
|
||||
|
||||
# ================== Redis ==================
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改
|
||||
port: 6379
|
||||
database: 0
|
||||
password: 123456 # 如果有密码就填上,没密码可以去掉这一行
|
||||
|
||||
# ================== SpringDoc ==================
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: true
|
||||
path: /v3/api-docs
|
||||
swagger-ui:
|
||||
enabled: true
|
||||
path: /swagger-ui.html
|
||||
group-configs:
|
||||
- group: 'default'
|
||||
display-name: 'AI代理服务 API'
|
||||
paths-to-match: '/**'
|
||||
|
||||
# ================== Dubbo + Nacos ==================
|
||||
dubbo:
|
||||
application:
|
||||
name: urban-lifeline-agent
|
||||
qos-enable: false
|
||||
protocol:
|
||||
name: dubbo
|
||||
port: -1
|
||||
registry:
|
||||
address: nacos://127.0.0.1:8848
|
||||
scan:
|
||||
base-packages: org.xyzh.agent.service.impl
|
||||
|
||||
# ================== MyBatis ==================
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath:mapper/**/*.xml
|
||||
type-aliases-package: org.xyzh.common.dto, org.xyzh.api
|
||||
|
||||
# ================== Logging ==================
|
||||
logging:
|
||||
config: classpath:log4j2.xml
|
||||
charset:
|
||||
console: UTF-8
|
||||
file: UTF-8
|
||||
85
urbanLifelineServ/agent/src/main/resources/log4j2.xml
Normal file
85
urbanLifelineServ/agent/src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration status="WARN" monitorInterval="30">
|
||||
<Properties>
|
||||
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
|
||||
<property name="FILE_PATH" value="./logs" />
|
||||
<property name="FILE_NAME" value="agent-service" />
|
||||
<property name="file.encoding" value="UTF-8" />
|
||||
<property name="console.encoding" value="UTF-8" />
|
||||
</Properties>
|
||||
|
||||
<appenders>
|
||||
<console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</console>
|
||||
|
||||
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/${FILE_NAME}-info.log"
|
||||
filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="100MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="30"/>
|
||||
</RollingFile>
|
||||
|
||||
<RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/${FILE_NAME}-warn.log"
|
||||
filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="100MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="30"/>
|
||||
</RollingFile>
|
||||
|
||||
<RollingFile name="RollingFileError" fileName="${FILE_PATH}/${FILE_NAME}-error.log"
|
||||
filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="100MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="30"/>
|
||||
</RollingFile>
|
||||
</appenders>
|
||||
|
||||
<loggers>
|
||||
<logger name="com.alibaba.nacos" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.mybatis" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</logger>
|
||||
|
||||
<Logger name="org.springframework" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
|
||||
<Logger name="org.xyzh.agent" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
</Logger>
|
||||
|
||||
<Logger name="org.xyzh.common" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
</Logger>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="Console"/>
|
||||
<appender-ref ref="RollingFileInfo"/>
|
||||
<appender-ref ref="RollingFileWarn"/>
|
||||
<appender-ref ref="RollingFileError"/>
|
||||
</root>
|
||||
</loggers>
|
||||
</configuration>
|
||||
21
urbanLifelineServ/apis/api-agent/pom.xml
Normal file
21
urbanLifelineServ/apis/api-agent/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>apis</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-agent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.xyzh.api.agent;
|
||||
|
||||
/**
|
||||
* Agent服务接口
|
||||
* 用于知识库和文档管理
|
||||
*/
|
||||
public interface AgentService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.xyzh.api.agent.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* 知识库DTO
|
||||
* 用于创建和更新知识库
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "知识库DTO")
|
||||
public class KnowledgeBaseDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "知识库ID(更新时需要)")
|
||||
private String knowledgeId;
|
||||
|
||||
@Schema(description = "智能体ID")
|
||||
private String agentId;
|
||||
|
||||
@Schema(description = "知识库名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "知识库类型:bidding-招投标/customer_service-客服/internal-内部协同")
|
||||
private String kbType;
|
||||
|
||||
@Schema(description = "访问级别:public-公开/private-私有/internal-内部")
|
||||
private String accessLevel;
|
||||
|
||||
@Schema(description = "知识库描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "存储路径")
|
||||
private String storagePath;
|
||||
|
||||
@Schema(description = "当前版本号")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "知识库配置")
|
||||
private JsonNode config;
|
||||
|
||||
@Schema(description = "服务类型")
|
||||
private String serviceType;
|
||||
|
||||
@Schema(description = "状态:active-激活/inactive-停用/archived-归档")
|
||||
private String status;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.xyzh.api.agent.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* 知识文档片段DTO
|
||||
* 用于创建和更新知识文档片段
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "知识文档片段DTO")
|
||||
public class KnowledgeChunkDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "片段ID(更新时需要)")
|
||||
private String chunkId;
|
||||
|
||||
@Schema(description = "所属文档ID")
|
||||
private String docId;
|
||||
|
||||
@Schema(description = "所属知识库ID")
|
||||
private String knowledgeId;
|
||||
|
||||
@Schema(description = "片段索引")
|
||||
private Integer chunkIndex;
|
||||
|
||||
@Schema(description = "片段内容")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "片段类型:text-文本/table-表格/image-图片")
|
||||
private String chunkType;
|
||||
|
||||
@Schema(description = "根chunk ID")
|
||||
private String rootChunkId;
|
||||
|
||||
@Schema(description = "是否当前版本", defaultValue = "true")
|
||||
private Boolean isCurrent;
|
||||
|
||||
@Schema(description = "位置信息")
|
||||
private JsonNode positionInfo;
|
||||
|
||||
@Schema(description = "片段元数据")
|
||||
private JsonNode metadata;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.xyzh.api.agent.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 知识文档DTO
|
||||
* 用于创建和更新知识文档
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "知识文档DTO")
|
||||
public class KnowledgeDocumentDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "文档ID(更新时需要)")
|
||||
private String docId;
|
||||
|
||||
@Schema(description = "所属知识库ID")
|
||||
private String knowledgeId;
|
||||
|
||||
@Schema(description = "文档标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "文档类型:text-文本/pdf/word/excel/image/video")
|
||||
private String docType;
|
||||
|
||||
@Schema(description = "文档分类")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "文档内容(文本类型)")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "关联文件ID")
|
||||
private String fileId;
|
||||
|
||||
@Schema(description = "文件路径")
|
||||
private String filePath;
|
||||
|
||||
@Schema(description = "文件大小")
|
||||
private Long fileSize;
|
||||
|
||||
@Schema(description = "MIME类型")
|
||||
private String mimeType;
|
||||
|
||||
@Schema(description = "根文档ID")
|
||||
private String rootDocId;
|
||||
|
||||
@Schema(description = "文档标签")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "来源URL")
|
||||
private String sourceUrl;
|
||||
|
||||
@Schema(description = "服务类型")
|
||||
private String serviceType;
|
||||
|
||||
@Schema(description = "文档元数据")
|
||||
private JsonNode metadata;
|
||||
|
||||
@Schema(description = "状态:active-激活/inactive-停用/archived-归档")
|
||||
private String status;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package org.xyzh.api.agent.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 知识库VO
|
||||
* 用于前端展示知识库信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "知识库VO")
|
||||
public class KnowledgeBaseVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "知识库ID")
|
||||
private String knowledgeId;
|
||||
|
||||
@Schema(description = "智能体ID")
|
||||
private String agentId;
|
||||
|
||||
@Schema(description = "智能体名称")
|
||||
private String agentName;
|
||||
|
||||
@Schema(description = "知识库名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "知识库类型")
|
||||
private String kbType;
|
||||
|
||||
@Schema(description = "知识库类型名称")
|
||||
private String kbTypeName;
|
||||
|
||||
@Schema(description = "访问级别")
|
||||
private String accessLevel;
|
||||
|
||||
@Schema(description = "访问级别名称")
|
||||
private String accessLevelName;
|
||||
|
||||
@Schema(description = "知识库描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "存储路径")
|
||||
private String storagePath;
|
||||
|
||||
@Schema(description = "当前版本号")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "知识库配置")
|
||||
private JsonNode config;
|
||||
|
||||
@Schema(description = "服务类型")
|
||||
private String serviceType;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "状态名称")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "状态颜色")
|
||||
private String statusColor;
|
||||
|
||||
@Schema(description = "文档总数")
|
||||
private Long documentCount;
|
||||
|
||||
@Schema(description = "已向量化文档数")
|
||||
private Long embeddedDocCount;
|
||||
|
||||
@Schema(description = "chunk总数")
|
||||
private Long chunkCount;
|
||||
|
||||
@Schema(description = "总存储大小(字节)")
|
||||
private Long totalSize;
|
||||
|
||||
@Schema(description = "总存储大小格式化显示")
|
||||
private String totalSizeFormatted;
|
||||
|
||||
@Schema(description = "最后同步时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastSyncTime;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.xyzh.api.agent.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 知识文档片段VO
|
||||
* 用于前端展示知识文档片段信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "知识文档片段VO")
|
||||
public class KnowledgeChunkVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "片段ID")
|
||||
private String chunkId;
|
||||
|
||||
@Schema(description = "所属文档ID")
|
||||
private String docId;
|
||||
|
||||
@Schema(description = "文档标题")
|
||||
private String docTitle;
|
||||
|
||||
@Schema(description = "所属知识库ID")
|
||||
private String knowledgeId;
|
||||
|
||||
@Schema(description = "知识库名称")
|
||||
private String knowledgeName;
|
||||
|
||||
@Schema(description = "片段索引")
|
||||
private Integer chunkIndex;
|
||||
|
||||
@Schema(description = "片段内容")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "内容长度")
|
||||
private Integer contentLength;
|
||||
|
||||
@Schema(description = "片段类型")
|
||||
private String chunkType;
|
||||
|
||||
@Schema(description = "片段类型名称")
|
||||
private String chunkTypeName;
|
||||
|
||||
@Schema(description = "版本号")
|
||||
private Integer version;
|
||||
|
||||
@Schema(description = "根chunk ID")
|
||||
private String rootChunkId;
|
||||
|
||||
@Schema(description = "是否当前版本", defaultValue = "false")
|
||||
private Boolean isCurrent;
|
||||
|
||||
@Schema(description = "位置信息")
|
||||
private JsonNode positionInfo;
|
||||
|
||||
@Schema(description = "片段元数据")
|
||||
private JsonNode metadata;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String deptName;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.xyzh.api.agent.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 知识文档VO
|
||||
* 用于前端展示知识文档信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "知识文档VO")
|
||||
public class KnowledgeDocumentVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "文档ID")
|
||||
private String docId;
|
||||
|
||||
@Schema(description = "所属知识库ID")
|
||||
private String knowledgeId;
|
||||
|
||||
@Schema(description = "知识库名称")
|
||||
private String knowledgeName;
|
||||
|
||||
@Schema(description = "文档标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "文档类型")
|
||||
private String docType;
|
||||
|
||||
@Schema(description = "文档类型名称")
|
||||
private String docTypeName;
|
||||
|
||||
@Schema(description = "文档分类")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "文档内容")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "内容摘要")
|
||||
private String contentSummary;
|
||||
|
||||
@Schema(description = "关联文件ID")
|
||||
private String fileId;
|
||||
|
||||
@Schema(description = "文件路径")
|
||||
private String filePath;
|
||||
|
||||
@Schema(description = "文件下载URL")
|
||||
private String fileUrl;
|
||||
|
||||
@Schema(description = "文件大小")
|
||||
private Long fileSize;
|
||||
|
||||
@Schema(description = "文件大小格式化显示")
|
||||
private String fileSizeFormatted;
|
||||
|
||||
@Schema(description = "MIME类型")
|
||||
private String mimeType;
|
||||
|
||||
@Schema(description = "版本号")
|
||||
private Integer version;
|
||||
|
||||
@Schema(description = "根文档ID")
|
||||
private String rootDocId;
|
||||
|
||||
@Schema(description = "文档标签")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "关键词")
|
||||
private List<String> keywords;
|
||||
|
||||
@Schema(description = "向量化状态")
|
||||
private String embeddingStatus;
|
||||
|
||||
@Schema(description = "向量化状态名称")
|
||||
private String embeddingStatusName;
|
||||
|
||||
@Schema(description = "使用的向量化模型")
|
||||
private String embeddingModel;
|
||||
|
||||
@Schema(description = "切片数量")
|
||||
private Integer chunkCount;
|
||||
|
||||
@Schema(description = "文档元数据")
|
||||
private JsonNode metadata;
|
||||
|
||||
@Schema(description = "来源URL")
|
||||
private String sourceUrl;
|
||||
|
||||
@Schema(description = "服务类型")
|
||||
private String serviceType;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "状态名称")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "更新者姓名")
|
||||
private String updaterName;
|
||||
}
|
||||
21
urbanLifelineServ/apis/api-bidding/pom.xml
Normal file
21
urbanLifelineServ/apis/api-bidding/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>apis</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-bidding</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.xyzh.api.bidding;
|
||||
|
||||
public interface BiddingService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.xyzh.api.bidding.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 投标文件DTO
|
||||
* 用于创建和更新投标文件
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "投标文件DTO")
|
||||
public class BidResponseDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "响应文件ID(更新时需要)")
|
||||
private String responseId;
|
||||
|
||||
@Schema(description = "所属项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "响应类型:technical-技术标/commercial-商务标/comprehensive-综合标")
|
||||
private String responseType;
|
||||
|
||||
@Schema(description = "文档名称")
|
||||
private String docName;
|
||||
|
||||
@Schema(description = "文档大纲")
|
||||
private String outline;
|
||||
|
||||
@Schema(description = "文档内容")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "生成方式:ai-AI生成/template-模板生成/manual-人工编写")
|
||||
private String generationMethod;
|
||||
|
||||
@Schema(description = "使用的模板ID")
|
||||
private String templateId;
|
||||
|
||||
@Schema(description = "使用的AI模型")
|
||||
private String aiModel;
|
||||
|
||||
@Schema(description = "生成状态:draft-草稿/reviewing-审核中/approved-已批准/rejected-已拒绝/submitted-已提交")
|
||||
private String generationStatus;
|
||||
|
||||
@Schema(description = "版本号")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "父版本ID")
|
||||
private String parentVersionId;
|
||||
|
||||
@Schema(description = "审核意见")
|
||||
private String reviewComments;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.xyzh.api.bidding.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 招标文档DTO
|
||||
* 用于创建和更新招标文档
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "招标文档DTO")
|
||||
public class BiddingDocumentDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "文档ID(更新时需要)")
|
||||
private String docId;
|
||||
|
||||
@Schema(description = "所属项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "文档类型:tender-招标文件/technical-技术标/commercial-商务标/clarification-澄清文件/other-其他")
|
||||
private String docType;
|
||||
|
||||
@Schema(description = "文档名称")
|
||||
private String docName;
|
||||
|
||||
@Schema(description = "关联文件ID")
|
||||
private String fileId;
|
||||
|
||||
@Schema(description = "文件路径")
|
||||
private String filePath;
|
||||
|
||||
@Schema(description = "文件大小")
|
||||
private Long fileSize;
|
||||
|
||||
@Schema(description = "MIME类型")
|
||||
private String mimeType;
|
||||
|
||||
@Schema(description = "版本号")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "语言")
|
||||
private String language;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package org.xyzh.api.bidding.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 招标项目DTO
|
||||
* 用于创建和更新招标项目
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "招标项目DTO")
|
||||
public class BiddingProjectDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "项目ID(更新时需要)")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "项目编号")
|
||||
private String projectNo;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "项目类型:public-公开招标/invitation-邀请招标/competitive_negotiation-竞争性谈判")
|
||||
private String projectType;
|
||||
|
||||
@Schema(description = "所属行业")
|
||||
private String industry;
|
||||
|
||||
@Schema(description = "来源平台")
|
||||
private String sourcePlatform;
|
||||
|
||||
@Schema(description = "来源URL")
|
||||
private String sourceUrl;
|
||||
|
||||
@Schema(description = "发布日期", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date publishDate;
|
||||
|
||||
@Schema(description = "投标截止日期", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date deadline;
|
||||
|
||||
@Schema(description = "开标日期", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date openingDate;
|
||||
|
||||
@Schema(description = "预算金额")
|
||||
private BigDecimal budgetAmount;
|
||||
|
||||
@Schema(description = "货币单位")
|
||||
private String currency;
|
||||
|
||||
@Schema(description = "项目状态")
|
||||
private String projectStatus;
|
||||
|
||||
@Schema(description = "中标状态")
|
||||
private String winningStatus;
|
||||
|
||||
@Schema(description = "中标金额")
|
||||
private BigDecimal winningAmount;
|
||||
|
||||
@Schema(description = "客户名称")
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "客户联系方式")
|
||||
private String clientContact;
|
||||
|
||||
@Schema(description = "联系人")
|
||||
private String contactPerson;
|
||||
|
||||
@Schema(description = "项目地点")
|
||||
private String projectLocation;
|
||||
|
||||
@Schema(description = "项目描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "关键词")
|
||||
private List<String> keywords;
|
||||
|
||||
@Schema(description = "负责人")
|
||||
private String responsibleUser;
|
||||
|
||||
@Schema(description = "团队成员")
|
||||
private List<String> teamMembers;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.xyzh.api.bidding.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 招标要素DTO
|
||||
* 用于创建和更新招标要素
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "招标要素DTO")
|
||||
public class BiddingRequirementDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "要素ID(更新时需要)")
|
||||
private String reqId;
|
||||
|
||||
@Schema(description = "所属项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "来源文档ID")
|
||||
private String docId;
|
||||
|
||||
@Schema(description = "要素类别:commercial-商务要素/technical-技术参数/veto-否决项/qualification-资质要求/delivery-交付要求/payment-付款条件/scoring-评分标准")
|
||||
private String reqCategory;
|
||||
|
||||
@Schema(description = "要素名称")
|
||||
private String reqName;
|
||||
|
||||
@Schema(description = "要素内容")
|
||||
private String reqContent;
|
||||
|
||||
@Schema(description = "要素值")
|
||||
private String reqValue;
|
||||
|
||||
@Schema(description = "是否必填", defaultValue = "false")
|
||||
private Boolean isMandatory;
|
||||
|
||||
@Schema(description = "是否为否决项", defaultValue = "false")
|
||||
private Boolean isVeto;
|
||||
|
||||
@Schema(description = "优先级")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "提取方式:ai-AI提取/manual-人工录入")
|
||||
private String extractionMethod;
|
||||
|
||||
@Schema(description = "置信度分数")
|
||||
private BigDecimal confidenceScore;
|
||||
|
||||
@Schema(description = "来源位置")
|
||||
private JsonNode sourceLocation;
|
||||
|
||||
@Schema(description = "合规状态:compliant-符合/non_compliant-不符合/pending-待确认")
|
||||
private String complianceStatus;
|
||||
|
||||
@Schema(description = "响应内容")
|
||||
private String responseContent;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String notes;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.xyzh.api.bidding.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 项目流程节点DTO
|
||||
* 用于创建和更新流程节点
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "项目流程节点DTO")
|
||||
public class ProcessNodeDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "流程节点ID(更新时需要)")
|
||||
private String processId;
|
||||
|
||||
@Schema(description = "所属项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "节点名称")
|
||||
private String nodeName;
|
||||
|
||||
@Schema(description = "节点类型:collection-文件收集/analysis-需求分析/preparation-文件准备/review-内部审核/submission-投标提交/opening-开标/result-结果通知")
|
||||
private String nodeType;
|
||||
|
||||
@Schema(description = "节点顺序")
|
||||
private Integer nodeOrder;
|
||||
|
||||
@Schema(description = "节点状态:pending-待处理/in_progress-进行中/completed-已完成/skipped-已跳过")
|
||||
private String nodeStatus;
|
||||
|
||||
@Schema(description = "计划开始时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date plannedStartTime;
|
||||
|
||||
@Schema(description = "计划结束时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date plannedEndTime;
|
||||
|
||||
@Schema(description = "实际开始时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date actualStartTime;
|
||||
|
||||
@Schema(description = "实际结束时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date actualEndTime;
|
||||
|
||||
@Schema(description = "负责人")
|
||||
private String responsibleUser;
|
||||
|
||||
@Schema(description = "参与人员")
|
||||
private List<String> participants;
|
||||
|
||||
@Schema(description = "节点备注")
|
||||
private String notes;
|
||||
|
||||
@Schema(description = "附件ID数组")
|
||||
private List<String> attachments;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.xyzh.api.bidding.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 投标文件VO
|
||||
* 用于前端展示投标文件信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "投标文件VO")
|
||||
public class BidResponseVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "响应文件ID")
|
||||
private String responseId;
|
||||
|
||||
@Schema(description = "所属项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "响应类型")
|
||||
private String responseType;
|
||||
|
||||
@Schema(description = "响应类型名称(中文)")
|
||||
private String responseTypeName;
|
||||
|
||||
@Schema(description = "文档名称")
|
||||
private String docName;
|
||||
|
||||
@Schema(description = "文档大纲")
|
||||
private String outline;
|
||||
|
||||
@Schema(description = "文档内容")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "内容字数")
|
||||
private Integer contentWordCount;
|
||||
|
||||
@Schema(description = "生成方式")
|
||||
private String generationMethod;
|
||||
|
||||
@Schema(description = "生成方式名称")
|
||||
private String generationMethodName;
|
||||
|
||||
@Schema(description = "使用的模板ID")
|
||||
private String templateId;
|
||||
|
||||
@Schema(description = "使用的模板名称")
|
||||
private String templateName;
|
||||
|
||||
@Schema(description = "使用的AI模型")
|
||||
private String aiModel;
|
||||
|
||||
@Schema(description = "生成状态")
|
||||
private String generationStatus;
|
||||
|
||||
@Schema(description = "生成状态名称")
|
||||
private String generationStatusName;
|
||||
|
||||
@Schema(description = "生成状态颜色标签")
|
||||
private String statusColor;
|
||||
|
||||
@Schema(description = "关联文件ID")
|
||||
private String fileId;
|
||||
|
||||
@Schema(description = "文件路径")
|
||||
private String filePath;
|
||||
|
||||
@Schema(description = "文件下载URL")
|
||||
private String fileUrl;
|
||||
|
||||
@Schema(description = "版本号")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "父版本ID")
|
||||
private String parentVersionId;
|
||||
|
||||
@Schema(description = "是否有历史版本", defaultValue = "false")
|
||||
private Boolean hasHistory;
|
||||
|
||||
@Schema(description = "审核意见")
|
||||
private String reviewComments;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "更新者姓名")
|
||||
private String updaterName;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.xyzh.api.bidding.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 招标文档VO
|
||||
* 用于前端展示招标文档信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "招标文档VO")
|
||||
public class BiddingDocumentVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "文档ID")
|
||||
private String docId;
|
||||
|
||||
@Schema(description = "所属项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "文档类型")
|
||||
private String docType;
|
||||
|
||||
@Schema(description = "文档类型名称(中文)")
|
||||
private String docTypeName;
|
||||
|
||||
@Schema(description = "文档名称")
|
||||
private String docName;
|
||||
|
||||
@Schema(description = "关联文件ID")
|
||||
private String fileId;
|
||||
|
||||
@Schema(description = "文件路径")
|
||||
private String filePath;
|
||||
|
||||
@Schema(description = "文件下载URL")
|
||||
private String fileUrl;
|
||||
|
||||
@Schema(description = "文件大小")
|
||||
private Long fileSize;
|
||||
|
||||
@Schema(description = "文件大小格式化显示")
|
||||
private String fileSizeFormatted;
|
||||
|
||||
@Schema(description = "MIME类型")
|
||||
private String mimeType;
|
||||
|
||||
@Schema(description = "文件类型图标")
|
||||
private String fileIcon;
|
||||
|
||||
@Schema(description = "版本号")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "语言")
|
||||
private String language;
|
||||
|
||||
@Schema(description = "页数")
|
||||
private Integer pageCount;
|
||||
|
||||
@Schema(description = "解析状态")
|
||||
private String parseStatus;
|
||||
|
||||
@Schema(description = "解析状态名称")
|
||||
private String parseStatusName;
|
||||
|
||||
@Schema(description = "解析结果")
|
||||
private JsonNode parseResult;
|
||||
|
||||
@Schema(description = "提取的结构化数据")
|
||||
private JsonNode extractionData;
|
||||
|
||||
@Schema(description = "AI分析结果")
|
||||
private String aiAnalysis;
|
||||
|
||||
@Schema(description = "上传日期", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date uploadDate;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package org.xyzh.api.bidding.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 招标项目VO
|
||||
* 用于前端展示招标项目详细信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "招标项目VO")
|
||||
public class BiddingProjectVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "项目编号")
|
||||
private String projectNo;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "项目类型")
|
||||
private String projectType;
|
||||
|
||||
@Schema(description = "项目类型名称(中文)")
|
||||
private String projectTypeName;
|
||||
|
||||
@Schema(description = "所属行业")
|
||||
private String industry;
|
||||
|
||||
@Schema(description = "来源平台")
|
||||
private String sourcePlatform;
|
||||
|
||||
@Schema(description = "来源URL")
|
||||
private String sourceUrl;
|
||||
|
||||
@Schema(description = "发布日期", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date publishDate;
|
||||
|
||||
@Schema(description = "投标截止日期", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date deadline;
|
||||
|
||||
@Schema(description = "开标日期", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date openingDate;
|
||||
|
||||
@Schema(description = "距离截止日期剩余天数")
|
||||
private Long daysUntilDeadline;
|
||||
|
||||
@Schema(description = "预算金额")
|
||||
private BigDecimal budgetAmount;
|
||||
|
||||
@Schema(description = "货币单位")
|
||||
private String currency;
|
||||
|
||||
@Schema(description = "预算金额格式化显示")
|
||||
private String budgetAmountFormatted;
|
||||
|
||||
@Schema(description = "项目状态")
|
||||
private String projectStatus;
|
||||
|
||||
@Schema(description = "项目状态名称(中文)")
|
||||
private String projectStatusName;
|
||||
|
||||
@Schema(description = "中标状态")
|
||||
private String winningStatus;
|
||||
|
||||
@Schema(description = "中标状态名称(中文)")
|
||||
private String winningStatusName;
|
||||
|
||||
@Schema(description = "中标金额")
|
||||
private BigDecimal winningAmount;
|
||||
|
||||
@Schema(description = "中标金额格式化显示")
|
||||
private String winningAmountFormatted;
|
||||
|
||||
@Schema(description = "客户名称")
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "客户联系方式")
|
||||
private String clientContact;
|
||||
|
||||
@Schema(description = "联系人")
|
||||
private String contactPerson;
|
||||
|
||||
@Schema(description = "项目地点")
|
||||
private String projectLocation;
|
||||
|
||||
@Schema(description = "项目描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "关键词")
|
||||
private List<String> keywords;
|
||||
|
||||
@Schema(description = "负责人ID")
|
||||
private String responsibleUser;
|
||||
|
||||
@Schema(description = "负责人姓名")
|
||||
private String responsibleUserName;
|
||||
|
||||
@Schema(description = "团队成员")
|
||||
private List<String> teamMembers;
|
||||
|
||||
@Schema(description = "团队成员姓名列表")
|
||||
private List<String> teamMemberNames;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "文档数量")
|
||||
private Integer documentCount;
|
||||
|
||||
@Schema(description = "要素数量")
|
||||
private Integer requirementCount;
|
||||
|
||||
@Schema(description = "否决项数量")
|
||||
private Integer vetoCount;
|
||||
|
||||
@Schema(description = "投标文件数量")
|
||||
private Integer responseCount;
|
||||
|
||||
@Schema(description = "完成进度(百分比)")
|
||||
private Integer progressPercentage;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package org.xyzh.api.bidding.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 招标要素VO
|
||||
* 用于前端展示招标要素信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "招标要素VO")
|
||||
public class BiddingRequirementVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "要素ID")
|
||||
private String reqId;
|
||||
|
||||
@Schema(description = "所属项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "来源文档ID")
|
||||
private String docId;
|
||||
|
||||
@Schema(description = "来源文档名称")
|
||||
private String docName;
|
||||
|
||||
@Schema(description = "要素类别")
|
||||
private String reqCategory;
|
||||
|
||||
@Schema(description = "要素类别名称(中文)")
|
||||
private String reqCategoryName;
|
||||
|
||||
@Schema(description = "要素名称")
|
||||
private String reqName;
|
||||
|
||||
@Schema(description = "要素内容")
|
||||
private String reqContent;
|
||||
|
||||
@Schema(description = "要素值")
|
||||
private String reqValue;
|
||||
|
||||
@Schema(description = "是否必填", defaultValue = "false")
|
||||
private Boolean isMandatory;
|
||||
|
||||
@Schema(description = "是否为否决项", defaultValue = "false")
|
||||
private Boolean isVeto;
|
||||
|
||||
@Schema(description = "优先级")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "优先级标签(高/中/低)")
|
||||
private String priorityLabel;
|
||||
|
||||
@Schema(description = "提取方式")
|
||||
private String extractionMethod;
|
||||
|
||||
@Schema(description = "提取方式名称")
|
||||
private String extractionMethodName;
|
||||
|
||||
@Schema(description = "置信度分数")
|
||||
private BigDecimal confidenceScore;
|
||||
|
||||
@Schema(description = "置信度百分比")
|
||||
private Integer confidencePercentage;
|
||||
|
||||
@Schema(description = "来源位置")
|
||||
private JsonNode sourceLocation;
|
||||
|
||||
@Schema(description = "来源位置描述")
|
||||
private String sourceLocationDesc;
|
||||
|
||||
@Schema(description = "合规状态")
|
||||
private String complianceStatus;
|
||||
|
||||
@Schema(description = "合规状态名称")
|
||||
private String complianceStatusName;
|
||||
|
||||
@Schema(description = "合规状态标签颜色")
|
||||
private String complianceStatusColor;
|
||||
|
||||
@Schema(description = "响应内容")
|
||||
private String responseContent;
|
||||
|
||||
@Schema(description = "是否已响应", defaultValue = "false")
|
||||
private Boolean hasResponse;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String notes;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package org.xyzh.api.bidding.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 项目流程节点VO
|
||||
* 用于前端展示流程节点信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "项目流程节点VO")
|
||||
public class ProcessNodeVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "流程节点ID")
|
||||
private String processId;
|
||||
|
||||
@Schema(description = "所属项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "节点名称")
|
||||
private String nodeName;
|
||||
|
||||
@Schema(description = "节点类型")
|
||||
private String nodeType;
|
||||
|
||||
@Schema(description = "节点类型名称")
|
||||
private String nodeTypeName;
|
||||
|
||||
@Schema(description = "节点顺序")
|
||||
private Integer nodeOrder;
|
||||
|
||||
@Schema(description = "节点状态")
|
||||
private String nodeStatus;
|
||||
|
||||
@Schema(description = "节点状态名称")
|
||||
private String nodeStatusName;
|
||||
|
||||
@Schema(description = "节点状态颜色")
|
||||
private String statusColor;
|
||||
|
||||
@Schema(description = "节点图标")
|
||||
private String nodeIcon;
|
||||
|
||||
@Schema(description = "计划开始时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date plannedStartTime;
|
||||
|
||||
@Schema(description = "计划结束时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date plannedEndTime;
|
||||
|
||||
@Schema(description = "实际开始时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date actualStartTime;
|
||||
|
||||
@Schema(description = "实际结束时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date actualEndTime;
|
||||
|
||||
@Schema(description = "计划耗时(天)")
|
||||
private Integer plannedDuration;
|
||||
|
||||
@Schema(description = "实际耗时(天)")
|
||||
private Integer actualDuration;
|
||||
|
||||
@Schema(description = "是否延期", defaultValue = "false")
|
||||
private Boolean isDelayed;
|
||||
|
||||
@Schema(description = "延期天数")
|
||||
private Integer delayedDays;
|
||||
|
||||
@Schema(description = "负责人ID")
|
||||
private String responsibleUser;
|
||||
|
||||
@Schema(description = "负责人姓名")
|
||||
private String responsibleUserName;
|
||||
|
||||
@Schema(description = "参与人员ID列表")
|
||||
private List<String> participants;
|
||||
|
||||
@Schema(description = "参与人员姓名列表")
|
||||
private List<String> participantNames;
|
||||
|
||||
@Schema(description = "节点备注")
|
||||
private String notes;
|
||||
|
||||
@Schema(description = "附件ID数组")
|
||||
private List<String> attachments;
|
||||
|
||||
@Schema(description = "附件数量")
|
||||
private Integer attachmentCount;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.xyzh.api.bidding.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 招标项目列表VO(简化版)
|
||||
* 用于前端列表展示
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "招标项目列表VO")
|
||||
public class ProjectListVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "项目ID")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "项目编号")
|
||||
private String projectNo;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "项目类型")
|
||||
private String projectType;
|
||||
|
||||
@Schema(description = "项目类型名称")
|
||||
private String projectTypeName;
|
||||
|
||||
@Schema(description = "所属行业")
|
||||
private String industry;
|
||||
|
||||
@Schema(description = "投标截止日期", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date deadline;
|
||||
|
||||
@Schema(description = "距离截止日期剩余天数")
|
||||
private Long daysUntilDeadline;
|
||||
|
||||
@Schema(description = "是否即将截止(<3天)", defaultValue = "false")
|
||||
private Boolean isUrgent;
|
||||
|
||||
@Schema(description = "预算金额格式化显示")
|
||||
private String budgetAmountFormatted;
|
||||
|
||||
@Schema(description = "项目状态")
|
||||
private String projectStatus;
|
||||
|
||||
@Schema(description = "项目状态名称")
|
||||
private String projectStatusName;
|
||||
|
||||
@Schema(description = "中标状态")
|
||||
private String winningStatus;
|
||||
|
||||
@Schema(description = "客户名称")
|
||||
private String clientName;
|
||||
|
||||
@Schema(description = "负责人姓名")
|
||||
private String responsibleUserName;
|
||||
|
||||
@Schema(description = "完成进度(百分比)")
|
||||
private Integer progressPercentage;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.xyzh.api.bidding.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目统计VO
|
||||
* 用于前端展示项目统计数据
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "项目统计VO")
|
||||
public class ProjectStatisticsVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "总项目数")
|
||||
private Long totalProjects;
|
||||
|
||||
@Schema(description = "进行中的项目数")
|
||||
private Long ongoingProjects;
|
||||
|
||||
@Schema(description = "已提交的项目数")
|
||||
private Long submittedProjects;
|
||||
|
||||
@Schema(description = "已中标的项目数")
|
||||
private Long wonProjects;
|
||||
|
||||
@Schema(description = "未中标的项目数")
|
||||
private Long lostProjects;
|
||||
|
||||
@Schema(description = "中标率(百分比)")
|
||||
private BigDecimal winRate;
|
||||
|
||||
@Schema(description = "即将截止的项目数(<3天)")
|
||||
private Long urgentProjects;
|
||||
|
||||
@Schema(description = "总预算金额")
|
||||
private BigDecimal totalBudget;
|
||||
|
||||
@Schema(description = "总中标金额")
|
||||
private BigDecimal totalWinningAmount;
|
||||
|
||||
@Schema(description = "按状态分组统计")
|
||||
private Map<String, Long> projectsByStatus;
|
||||
|
||||
@Schema(description = "按类型分组统计")
|
||||
private Map<String, Long> projectsByType;
|
||||
|
||||
@Schema(description = "按行业分组统计")
|
||||
private Map<String, Long> projectsByIndustry;
|
||||
|
||||
@Schema(description = "按月份分组统计(最近12个月)")
|
||||
private Map<String, Long> projectsByMonth;
|
||||
|
||||
@Schema(description = "按负责人分组统计")
|
||||
private Map<String, Long> projectsByUser;
|
||||
|
||||
@Schema(description = "平均项目周期(天)")
|
||||
private Integer avgProjectCycle;
|
||||
|
||||
@Schema(description = "最近7天新增项目数")
|
||||
private Long recentNewProjects;
|
||||
}
|
||||
21
urbanLifelineServ/apis/api-crontab/pom.xml
Normal file
21
urbanLifelineServ/apis/api-crontab/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>apis</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-crontab</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.xyzh.api.crontab;
|
||||
|
||||
/**
|
||||
* 定时任务服务接口
|
||||
* 用于定时任务管理
|
||||
*/
|
||||
public interface CrontabService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.xyzh.api.crontab.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 定时任务执行日志DTO
|
||||
* 用于记录任务执行情况
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "定时任务执行日志DTO")
|
||||
public class TbCrontabLogDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "任务ID")
|
||||
private String taskId;
|
||||
|
||||
@Schema(description = "任务名称")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "任务分组")
|
||||
private String taskGroup;
|
||||
|
||||
@Schema(description = "Bean名称")
|
||||
private String beanName;
|
||||
|
||||
@Schema(description = "方法名称")
|
||||
private String methodName;
|
||||
|
||||
@Schema(description = "方法参数")
|
||||
private String methodParams;
|
||||
|
||||
@Schema(description = "执行状态:0-失败/1-成功")
|
||||
private Integer executeStatus;
|
||||
|
||||
@Schema(description = "执行结果信息")
|
||||
private String executeMessage;
|
||||
|
||||
@Schema(description = "异常信息")
|
||||
private String exceptionInfo;
|
||||
|
||||
@Schema(description = "开始时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
|
||||
@Schema(description = "结束时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date endTime;
|
||||
|
||||
@Schema(description = "执行时长(毫秒)")
|
||||
private Integer executeDuration;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.xyzh.api.crontab.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 定时任务DTO
|
||||
* 用于创建和更新定时任务
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "定时任务DTO")
|
||||
public class TbCrontabTaskDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "任务ID(更新时需要)")
|
||||
private String taskId;
|
||||
|
||||
@Schema(description = "任务名称")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "任务分组", defaultValue = "DEFAULT")
|
||||
private String taskGroup;
|
||||
|
||||
@Schema(description = "任务元数据ID")
|
||||
private String metaId;
|
||||
|
||||
@Schema(description = "是否使用默认接收人", defaultValue = "false")
|
||||
private Boolean defaultRecipient;
|
||||
|
||||
@Schema(description = "Bean名称")
|
||||
private String beanName;
|
||||
|
||||
@Schema(description = "方法名称")
|
||||
private String methodName;
|
||||
|
||||
@Schema(description = "方法参数")
|
||||
private String methodParams;
|
||||
|
||||
@Schema(description = "Cron表达式")
|
||||
private String cronExpression;
|
||||
|
||||
@Schema(description = "任务状态:0-暂停/1-运行中", defaultValue = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "任务描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "是否允许并发执行", defaultValue = "false")
|
||||
private Boolean concurrent;
|
||||
|
||||
@Schema(description = "错过执行策略:1-立即执行/2-执行一次/3-放弃执行", defaultValue = "1")
|
||||
private Integer misfirePolicy;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.xyzh.api.crontab.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 定时任务元数据DTO
|
||||
* 用于创建和更新任务元数据配置
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "定时任务元数据DTO")
|
||||
public class TbCrontabTaskMetaDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "元数据ID(更新时需要)")
|
||||
private String metaId;
|
||||
|
||||
@Schema(description = "任务名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "任务描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "任务分类")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "Bean名称(执行器类名)")
|
||||
private String beanName;
|
||||
|
||||
@Schema(description = "执行方法名")
|
||||
private String methodName;
|
||||
|
||||
@Schema(description = "Python脚本路径")
|
||||
private String scriptPath;
|
||||
|
||||
@Schema(description = "参数模板(JSON格式)")
|
||||
private String paramSchema;
|
||||
|
||||
@Schema(description = "是否自动发布", defaultValue = "false")
|
||||
private Boolean autoPublish;
|
||||
|
||||
@Schema(description = "排序号", defaultValue = "0")
|
||||
private Integer sortOrder;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.xyzh.api.crontab.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 定时任务执行日志VO
|
||||
* 用于前端展示任务执行日志
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "定时任务执行日志VO")
|
||||
public class CrontabLogVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "日志ID")
|
||||
private String logId;
|
||||
|
||||
@Schema(description = "任务ID")
|
||||
private String taskId;
|
||||
|
||||
@Schema(description = "任务名称")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "任务分组")
|
||||
private String taskGroup;
|
||||
|
||||
@Schema(description = "Bean名称")
|
||||
private String beanName;
|
||||
|
||||
@Schema(description = "方法名称")
|
||||
private String methodName;
|
||||
|
||||
@Schema(description = "方法参数")
|
||||
private String methodParams;
|
||||
|
||||
@Schema(description = "执行状态:0-失败/1-成功")
|
||||
private Integer executeStatus;
|
||||
|
||||
@Schema(description = "执行状态名称")
|
||||
private String executeStatusName;
|
||||
|
||||
@Schema(description = "执行状态颜色")
|
||||
private String executeStatusColor;
|
||||
|
||||
@Schema(description = "执行结果信息")
|
||||
private String executeMessage;
|
||||
|
||||
@Schema(description = "异常信息")
|
||||
private String exceptionInfo;
|
||||
|
||||
@Schema(description = "是否有异常", defaultValue = "false")
|
||||
private Boolean hasException;
|
||||
|
||||
@Schema(description = "开始时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
|
||||
@Schema(description = "结束时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date endTime;
|
||||
|
||||
@Schema(description = "执行时长(毫秒)")
|
||||
private Integer executeDuration;
|
||||
|
||||
@Schema(description = "执行时长格式化显示")
|
||||
private String executeDurationFormatted;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.xyzh.api.crontab.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 定时任务列表VO
|
||||
* 用于前端列表展示(简化版)
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "定时任务列表VO")
|
||||
public class CrontabTaskListVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "任务ID")
|
||||
private String taskId;
|
||||
|
||||
@Schema(description = "任务名称")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "任务分组")
|
||||
private String taskGroup;
|
||||
|
||||
@Schema(description = "Cron表达式")
|
||||
private String cronExpression;
|
||||
|
||||
@Schema(description = "Cron表达式描述")
|
||||
private String cronDescription;
|
||||
|
||||
@Schema(description = "任务状态")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "任务状态名称")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "下次执行时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date nextExecuteTime;
|
||||
|
||||
@Schema(description = "最后执行时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastExecuteTime;
|
||||
|
||||
@Schema(description = "最后执行状态")
|
||||
private Integer lastExecuteStatus;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.xyzh.api.crontab.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 定时任务元数据VO
|
||||
* 用于前端展示任务元数据信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "定时任务元数据VO")
|
||||
public class CrontabTaskMetaVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "元数据ID")
|
||||
private String metaId;
|
||||
|
||||
@Schema(description = "任务名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "任务描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "任务分类")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "Bean名称(执行器类名)")
|
||||
private String beanName;
|
||||
|
||||
@Schema(description = "执行方法名")
|
||||
private String methodName;
|
||||
|
||||
@Schema(description = "Python脚本路径")
|
||||
private String scriptPath;
|
||||
|
||||
@Schema(description = "参数模板(JSON格式)")
|
||||
private String paramSchema;
|
||||
|
||||
@Schema(description = "是否自动发布", defaultValue = "false")
|
||||
private Boolean autoPublish;
|
||||
|
||||
@Schema(description = "排序号")
|
||||
private Integer sortOrder;
|
||||
|
||||
@Schema(description = "关联的任务数量")
|
||||
private Integer taskCount;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "更新者姓名")
|
||||
private String updaterName;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.xyzh.api.crontab.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 定时任务VO
|
||||
* 用于前端展示定时任务信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "定时任务VO")
|
||||
public class CrontabTaskVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "任务ID")
|
||||
private String taskId;
|
||||
|
||||
@Schema(description = "任务名称")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "任务分组")
|
||||
private String taskGroup;
|
||||
|
||||
@Schema(description = "任务元数据ID")
|
||||
private String metaId;
|
||||
|
||||
@Schema(description = "元数据名称")
|
||||
private String metaName;
|
||||
|
||||
@Schema(description = "是否使用默认接收人", defaultValue = "false")
|
||||
private Boolean defaultRecipient;
|
||||
|
||||
@Schema(description = "Bean名称")
|
||||
private String beanName;
|
||||
|
||||
@Schema(description = "方法名称")
|
||||
private String methodName;
|
||||
|
||||
@Schema(description = "方法参数")
|
||||
private String methodParams;
|
||||
|
||||
@Schema(description = "Cron表达式")
|
||||
private String cronExpression;
|
||||
|
||||
@Schema(description = "Cron表达式描述(中文)")
|
||||
private String cronDescription;
|
||||
|
||||
@Schema(description = "任务状态")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "任务状态名称")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "任务描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "是否允许并发执行", defaultValue = "false")
|
||||
private Boolean concurrent;
|
||||
|
||||
@Schema(description = "错过执行策略")
|
||||
private Integer misfirePolicy;
|
||||
|
||||
@Schema(description = "错过执行策略名称")
|
||||
private String misfirePolicyName;
|
||||
|
||||
@Schema(description = "下次执行时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date nextExecuteTime;
|
||||
|
||||
@Schema(description = "最后执行时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastExecuteTime;
|
||||
|
||||
@Schema(description = "最后执行状态:0-失败/1-成功")
|
||||
private Integer lastExecuteStatus;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "更新者姓名")
|
||||
private String updaterName;
|
||||
}
|
||||
@@ -14,8 +14,20 @@ import org.xyzh.common.core.page.PageParam;
|
||||
* @since 2025-11-05
|
||||
*/
|
||||
public interface MessageService {
|
||||
|
||||
// ================ 发送邮件 ==================
|
||||
ResultDomain<String> sendSimpleEmail(String to, String subject, String content);
|
||||
|
||||
ResultDomain<String> sendHtmlEmail(String to, String subject, String content);
|
||||
|
||||
ResultDomain<String> sendEmailVerificationCode(String to, String code);
|
||||
|
||||
// ================ 发送短信 ==================
|
||||
|
||||
//================= 用户查看消息列表 =================
|
||||
ResultDomain<String> sendPhoneVerificationCode(String phone, String code);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @description 获取我的消息列表
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.xyzh.api.message.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.util.Date;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
|
||||
/**
|
||||
* 用户消息接收记录VO
|
||||
* 用于前端展示用户消息信息
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "用户消息接收记录VO")
|
||||
public class MessageReceiverVO 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 typeName;
|
||||
|
||||
@Schema(description = "用户ID")
|
||||
private String userId;
|
||||
|
||||
@Schema(description = "用户姓名")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "接收渠道")
|
||||
private String channel;
|
||||
|
||||
@Schema(description = "渠道名称")
|
||||
private String channelName;
|
||||
|
||||
@Schema(description = "消息状态:unread-未读/read-已读/handled-已处理/deleted-已删除")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "状态名称")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "状态颜色")
|
||||
private String statusColor;
|
||||
|
||||
@Schema(description = "阅读时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date readTime;
|
||||
|
||||
@Schema(description = "处理时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date handleTime;
|
||||
|
||||
@Schema(description = "服务类型")
|
||||
private String serviceType;
|
||||
}
|
||||
21
urbanLifelineServ/apis/api-platform/pom.xml
Normal file
21
urbanLifelineServ/apis/api-platform/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>apis</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-platform</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.xyzh;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello world!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package org.xyzh.api.system.service;
|
||||
|
||||
import org.xyzh.api.system.vo.AclVO;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.page.PageRequest;
|
||||
import org.xyzh.common.dto.sys.TbSysAclDTO;
|
||||
import org.xyzh.common.dto.sys.TbSysAclPolicyDTO;
|
||||
|
||||
/**
|
||||
* @description 访问控制列表服务接口
|
||||
* @filename AclService.java
|
||||
* @author yslg
|
||||
* @copyright yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
public interface AclService {
|
||||
|
||||
// ================= ACL 管理 =================
|
||||
/**
|
||||
* @description 插入访问控制列表
|
||||
* @param aclDTO 访问控制列表DTO
|
||||
* @return ResultDomain<TbSysAclDTO> 插入结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<TbSysAclDTO> insertAcl(TbSysAclDTO aclDTO);
|
||||
|
||||
/**
|
||||
* @description 更新访问控制列表
|
||||
* @param aclDTO 访问控制列表DTO
|
||||
* @return ResultDomain<TbSysAclDTO> 更新结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<TbSysAclDTO> updateAcl(TbSysAclDTO aclDTO);
|
||||
|
||||
/**
|
||||
* @description 删除访问控制列表
|
||||
* @param aclDTO 访问控制列表DTO
|
||||
* @return ResultDomain<Boolean> 删除结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<Boolean> deleteAcl(TbSysAclDTO aclDTO);
|
||||
|
||||
/**
|
||||
* @description 根据条件查询访问控制列表分页数据
|
||||
* @param pageRequest 分页请求
|
||||
* @return ResultDomain<AclVO> 分页结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<AclVO> getAclPage(PageRequest<AclVO> pageRequest);
|
||||
|
||||
/**
|
||||
* @description 根据条件查询访问控制列表
|
||||
* @param filter 过滤条件
|
||||
* @return ResultDomain<AclVO> 查询结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<AclVO> getAclList(AclVO filter);
|
||||
|
||||
/**
|
||||
* @description 根据对象ID查询访问控制列表
|
||||
* @param objectId 对象ID
|
||||
* @return ResultDomain<AclVO> 查询结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<AclVO> getAclByObjectId(String objectId);
|
||||
|
||||
// ================= ACL Policy 管理 =================
|
||||
/**
|
||||
* @description 插入访问控制策略
|
||||
* @param aclPolicyDTO 访问控制策略DTO
|
||||
* @return ResultDomain<TbSysAclPolicyDTO> 插入结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<TbSysAclPolicyDTO> insertAclPolicy(TbSysAclPolicyDTO aclPolicyDTO);
|
||||
|
||||
/**
|
||||
* @description 更新访问控制策略
|
||||
* @param aclPolicyDTO 访问控制策略DTO
|
||||
* @return ResultDomain<TbSysAclPolicyDTO> 更新结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<TbSysAclPolicyDTO> updateAclPolicy(TbSysAclPolicyDTO aclPolicyDTO);
|
||||
|
||||
/**
|
||||
* @description 删除访问控制策略
|
||||
* @param aclPolicyDTO 访问控制策略DTO
|
||||
* @return ResultDomain<Boolean> 删除结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<Boolean> deleteAclPolicy(TbSysAclPolicyDTO aclPolicyDTO);
|
||||
|
||||
/**
|
||||
* @description 根据条件查询访问控制策略分页数据
|
||||
* @param pageRequest 分页请求
|
||||
* @return ResultDomain<AclVO> 分页结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<AclVO> getAclPolicyPage(PageRequest<AclVO> pageRequest);
|
||||
|
||||
/**
|
||||
* @description 根据条件查询访问控制策略列表
|
||||
* @param filter 过滤条件
|
||||
* @return ResultDomain<AclVO> 查询结果
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
ResultDomain<AclVO> getAclPolicyList(AclVO filter);
|
||||
}
|
||||
@@ -154,60 +154,6 @@ public interface DeptRoleService {
|
||||
*/
|
||||
ResultDomain<UserDeptRoleVO> getRoleListByUserId(String userId);
|
||||
|
||||
// ================= 部门角色关联管理 =================
|
||||
/**
|
||||
* @description 插入部门角色关联
|
||||
* @param deptRoleDTO 部门角色DTO
|
||||
* @return ResultDomain<TbSysDeptRoleDTO> 插入结果
|
||||
* @author yslg
|
||||
* @since 2025-11-05
|
||||
*/
|
||||
ResultDomain<TbSysDeptRoleDTO> insertDeptRole(TbSysDeptRoleDTO deptRoleDTO);
|
||||
|
||||
/**
|
||||
* @description 更新部门角色关联
|
||||
* @param deptRoleDTO 部门角色DTO
|
||||
* @return ResultDomain<TbSysDeptRoleDTO> 更新结果
|
||||
* @author yslg
|
||||
* @since 2025-11-05
|
||||
*/
|
||||
ResultDomain<TbSysDeptRoleDTO> updateDeptRole(TbSysDeptRoleDTO deptRoleDTO);
|
||||
|
||||
/**
|
||||
* @description 根据ID删除部门角色关联
|
||||
* @param deptRoleDTO 部门角色DTO
|
||||
* @return ResultDomain<Boolean> 删除结果
|
||||
* @author yslg
|
||||
* @since 2025-11-05
|
||||
*/
|
||||
ResultDomain<Boolean> deleteDeptRole(TbSysDeptRoleDTO deptRoleDTO);
|
||||
|
||||
/**
|
||||
* @description 根据ID查询部门角色关联
|
||||
* @param filter 部门角色VO
|
||||
* @return ResultDomain<UserDeptRoleVO> 查询结果
|
||||
* @author yslg
|
||||
* @since 2025-11-05
|
||||
*/
|
||||
ResultDomain<UserDeptRoleVO> getDeptRole(UserDeptRoleVO filter);
|
||||
|
||||
/**
|
||||
* @description 根据条件查询部门角色关联列表
|
||||
* @param filter 部门角色VO
|
||||
* @return ResultDomain<UserDeptRoleVO> 查询结果
|
||||
* @author yslg
|
||||
* @since 2025-11-05
|
||||
*/
|
||||
ResultDomain<UserDeptRoleVO> getDeptRoleList(UserDeptRoleVO filter);
|
||||
|
||||
/**
|
||||
* @description 根据条件查询部门角色关联分页列表
|
||||
* @param pageRequest 部门角色VO
|
||||
* @return ResultDomain<UserDeptRoleVO> 查询结果
|
||||
* @author yslg
|
||||
* @since 2025-11-05
|
||||
*/
|
||||
ResultDomain<UserDeptRoleVO> getDeptRolePage(PageRequest<UserDeptRoleVO> pageRequest);
|
||||
|
||||
// ==================== 角色权限关联 ================================
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,6 @@ 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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,43 @@ import org.xyzh.common.dto.sys.TbSysConfigDTO;
|
||||
* @since 2025-11-05
|
||||
*/
|
||||
public interface SysConfigService {
|
||||
// ================== 读取配置 ========================
|
||||
/**
|
||||
* 获取字符串类型配置
|
||||
* @param key 配置键
|
||||
* @return 配置值
|
||||
*/
|
||||
String getStringConfig(String key);
|
||||
|
||||
/**
|
||||
* 获取整数类型配置
|
||||
* @param key 配置键
|
||||
* @return 配置值,如果不存在或解析失败返回null
|
||||
*/
|
||||
Integer getIntConfig(String key);
|
||||
|
||||
/**
|
||||
* 获取布尔类型配置
|
||||
* @param key 配置键
|
||||
* @return 配置值,如果不存在或解析失败返回null
|
||||
*/
|
||||
Boolean getBooleanConfig(String key);
|
||||
|
||||
/**
|
||||
* 获取浮点数类型配置
|
||||
* @param key 配置键
|
||||
* @return 配置值,如果不存在或解析失败返回null
|
||||
*/
|
||||
Double getDoubleConfig(String key);
|
||||
|
||||
/**
|
||||
* 获取长整数类型配置
|
||||
* @param key 配置键
|
||||
* @return 配置值,如果不存在或解析失败返回null
|
||||
*/
|
||||
Long getLongConfig(String key);
|
||||
|
||||
// =====================================================
|
||||
|
||||
/**
|
||||
* @description 插入系统配置
|
||||
|
||||
@@ -18,6 +18,10 @@ import org.xyzh.common.dto.sys.TbSysUserRoleDTO;
|
||||
public interface SysUserService {
|
||||
|
||||
// ================= 用户基本信息管理 =================
|
||||
|
||||
ResultDomain<TbSysUserDTO> registerUser(SysUserVO userVO);
|
||||
|
||||
|
||||
/**
|
||||
* @description 插入用户
|
||||
* @param userVO 用户VO
|
||||
@@ -61,7 +65,7 @@ public interface SysUserService {
|
||||
* @author yslg
|
||||
* @since 2025-11-05
|
||||
*/
|
||||
ResultDomain<SysUserVO> getLoginUser(SysUserVO filter);
|
||||
ResultDomain<TbSysUserDTO> getLoginUser(TbSysUserDTO filter);
|
||||
|
||||
/**
|
||||
* @description 根据条件查询用户列表
|
||||
|
||||
@@ -24,9 +24,6 @@ public class UserDeptRoleVO extends BaseVO {
|
||||
@Schema(description = "用户ID")
|
||||
private String userId;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
@@ -49,18 +46,12 @@ public class UserDeptRoleVO extends BaseVO {
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@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;
|
||||
|
||||
@@ -134,7 +125,7 @@ public class UserDeptRoleVO extends BaseVO {
|
||||
dto.setDescription(vo.getRoleDescription());
|
||||
dto.setScope(vo.getScope());
|
||||
dto.setOwnerDeptId(vo.getOwnerDeptId());
|
||||
dto.setStatus(vo.isRoleStatus());
|
||||
dto.setStatus(vo.getRoleStatus());
|
||||
dto.setOptsn(vo.getOptsn());
|
||||
dto.setCreator(vo.getCreator());
|
||||
dto.setUpdater(vo.getUpdater());
|
||||
@@ -180,7 +171,7 @@ public class UserDeptRoleVO extends BaseVO {
|
||||
vo.setRoleDescription(permissionVO.getRoleDescription());
|
||||
vo.setScope(permissionVO.getRoleScope());
|
||||
vo.setOwnerDeptId(permissionVO.getRoleOwnerDeptId());
|
||||
vo.setRoleStatus(permissionVO.isRoleStatus());
|
||||
vo.setRoleStatus(permissionVO.getRoleStatus());
|
||||
vo.setOptsn(permissionVO.getOptsn());
|
||||
vo.setCreator(permissionVO.getCreator());
|
||||
vo.setUpdater(permissionVO.getUpdater());
|
||||
|
||||
21
urbanLifelineServ/apis/api-workcase/pom.xml
Normal file
21
urbanLifelineServ/apis/api-workcase/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>apis</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>api-workcase</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.xyzh;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello world!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.xyzh.api.workcase;
|
||||
|
||||
/**
|
||||
* 工单服务接口
|
||||
* 用于客服工单管理
|
||||
*/
|
||||
public interface WorkcaseService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.xyzh.api.workcase.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 会话DTO
|
||||
* 用于创建和更新会话
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "会话DTO")
|
||||
public class TbConversationDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "会话ID(更新时需要)")
|
||||
private String conversationId;
|
||||
|
||||
@Schema(description = "客户ID")
|
||||
private String customerId;
|
||||
|
||||
@Schema(description = "会话类型:ai-AI客服/human-人工客服/transfer-转接", defaultValue = "ai")
|
||||
private String conversationType;
|
||||
|
||||
@Schema(description = "渠道:wechat-微信/web-网页/app-应用/phone-电话", defaultValue = "wechat")
|
||||
private String channel;
|
||||
|
||||
@Schema(description = "智能体ID或客服人员ID")
|
||||
private String agentId;
|
||||
|
||||
@Schema(description = "座席类型:ai-AI/human-人工", defaultValue = "ai")
|
||||
private String agentType;
|
||||
|
||||
@Schema(description = "会话开始时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date sessionStartTime;
|
||||
|
||||
@Schema(description = "会话结束时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date sessionEndTime;
|
||||
|
||||
@Schema(description = "会话时长(秒)")
|
||||
private Integer durationSeconds;
|
||||
|
||||
@Schema(description = "消息数量", defaultValue = "0")
|
||||
private Integer messageCount;
|
||||
|
||||
@Schema(description = "会话状态:active-进行中/closed-已结束/transferred-已转接/timeout-超时", defaultValue = "active")
|
||||
private String conversationStatus;
|
||||
|
||||
@Schema(description = "满意度评分(1-5星)")
|
||||
private Integer satisfactionRating;
|
||||
|
||||
@Schema(description = "满意度反馈")
|
||||
private String satisfactionFeedback;
|
||||
|
||||
@Schema(description = "会话摘要(AI生成)")
|
||||
private String summary;
|
||||
|
||||
@Schema(description = "会话标签")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "会话元数据")
|
||||
private JsonNode metadata;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.xyzh.api.workcase.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客户信息DTO
|
||||
* 用于创建和更新客户信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "客户信息DTO")
|
||||
public class TbCustomerDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "客户ID(更新时需要)")
|
||||
private String customerId;
|
||||
|
||||
@Schema(description = "客户编号")
|
||||
private String customerNo;
|
||||
|
||||
@Schema(description = "客户姓名")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "客户类型:individual-个人/enterprise-企业", defaultValue = "individual")
|
||||
private String customerType;
|
||||
|
||||
@Schema(description = "公司名称")
|
||||
private String companyName;
|
||||
|
||||
@Schema(description = "电话")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "微信OpenID")
|
||||
private String wechatOpenid;
|
||||
|
||||
@Schema(description = "微信UnionID")
|
||||
private String wechatUnionid;
|
||||
|
||||
@Schema(description = "头像URL")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "性别:0-未知/1-男/2-女", defaultValue = "0")
|
||||
private Integer gender;
|
||||
|
||||
@Schema(description = "地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "客户等级:vip/important/normal/potential", defaultValue = "normal")
|
||||
private String customerLevel;
|
||||
|
||||
@Schema(description = "客户来源:wechat-微信/web-网站/phone-电话/referral-推荐")
|
||||
private String customerSource;
|
||||
|
||||
@Schema(description = "客户标签数组")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String notes;
|
||||
|
||||
@Schema(description = "CRM系统客户ID")
|
||||
private String crmCustomerId;
|
||||
|
||||
@Schema(description = "最后联系时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastContactTime;
|
||||
|
||||
@Schema(description = "咨询总次数", defaultValue = "0")
|
||||
private Integer totalConsultations;
|
||||
|
||||
@Schema(description = "订单总数", defaultValue = "0")
|
||||
private Integer totalOrders;
|
||||
|
||||
@Schema(description = "总消费金额", defaultValue = "0")
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
@Schema(description = "满意度评分(1-5)")
|
||||
private BigDecimal satisfactionScore;
|
||||
|
||||
@Schema(description = "状态:active-活跃/inactive-非活跃/blacklist-黑名单", defaultValue = "active")
|
||||
private String status;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.xyzh.api.workcase.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工单DTO
|
||||
* 用于创建和更新工单
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "工单DTO")
|
||||
public class TbTicketDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "工单ID(更新时需要)")
|
||||
private String ticketId;
|
||||
|
||||
@Schema(description = "工单编号")
|
||||
private String ticketNo;
|
||||
|
||||
@Schema(description = "客户ID")
|
||||
private String customerId;
|
||||
|
||||
@Schema(description = "关联会话ID")
|
||||
private String conversationId;
|
||||
|
||||
@Schema(description = "工单类型:consultation-咨询/complaint-投诉/suggestion-建议/repair-维修/installation-安装/other-其他")
|
||||
private String ticketType;
|
||||
|
||||
@Schema(description = "工单分类")
|
||||
private String ticketCategory;
|
||||
|
||||
@Schema(description = "优先级:urgent-紧急/high-高/normal-普通/low-低", defaultValue = "normal")
|
||||
private String priority;
|
||||
|
||||
@Schema(description = "工单标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "问题描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "附件ID数组")
|
||||
private List<String> attachments;
|
||||
|
||||
@Schema(description = "工单来源:ai-AI生成/manual-人工创建/system-系统自动", defaultValue = "ai")
|
||||
private String ticketSource;
|
||||
|
||||
@Schema(description = "分配给(处理人)")
|
||||
private String assignedTo;
|
||||
|
||||
@Schema(description = "分配部门")
|
||||
private String assignedDept;
|
||||
|
||||
@Schema(description = "工单状态:pending-待处理/processing-处理中/resolved-已解决/closed-已关闭/cancelled-已取消", defaultValue = "pending")
|
||||
private String ticketStatus;
|
||||
|
||||
@Schema(description = "解决方案")
|
||||
private String resolution;
|
||||
|
||||
@Schema(description = "解决时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date resolutionTime;
|
||||
|
||||
@Schema(description = "关闭时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date closeTime;
|
||||
|
||||
@Schema(description = "首次响应时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date responseTime;
|
||||
|
||||
@Schema(description = "SLA截止时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date slaDeadline;
|
||||
|
||||
@Schema(description = "是否逾期", defaultValue = "false")
|
||||
private Boolean isOverdue;
|
||||
|
||||
@Schema(description = "客户评分(1-5星)")
|
||||
private Integer customerRating;
|
||||
|
||||
@Schema(description = "客户反馈")
|
||||
private String customerFeedback;
|
||||
|
||||
@Schema(description = "CRM系统工单ID")
|
||||
private String crmTicketId;
|
||||
|
||||
@Schema(description = "同步状态:pending-待同步/synced-已同步/failed-失败", defaultValue = "pending")
|
||||
private String syncStatus;
|
||||
|
||||
@Schema(description = "工单标签")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "工单元数据")
|
||||
private JsonNode metadata;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package org.xyzh.api.workcase.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 会话VO
|
||||
* 用于前端展示会话信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "会话VO")
|
||||
public class ConversationVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "会话ID")
|
||||
private String conversationId;
|
||||
|
||||
@Schema(description = "客户ID")
|
||||
private String customerId;
|
||||
|
||||
@Schema(description = "客户姓名")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "客户头像")
|
||||
private String customerAvatar;
|
||||
|
||||
@Schema(description = "会话类型")
|
||||
private String conversationType;
|
||||
|
||||
@Schema(description = "会话类型名称")
|
||||
private String conversationTypeName;
|
||||
|
||||
@Schema(description = "渠道")
|
||||
private String channel;
|
||||
|
||||
@Schema(description = "渠道名称")
|
||||
private String channelName;
|
||||
|
||||
@Schema(description = "智能体ID或客服人员ID")
|
||||
private String agentId;
|
||||
|
||||
@Schema(description = "座席名称")
|
||||
private String agentName;
|
||||
|
||||
@Schema(description = "座席类型")
|
||||
private String agentType;
|
||||
|
||||
@Schema(description = "座席类型名称")
|
||||
private String agentTypeName;
|
||||
|
||||
@Schema(description = "会话开始时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date sessionStartTime;
|
||||
|
||||
@Schema(description = "会话结束时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date sessionEndTime;
|
||||
|
||||
@Schema(description = "会话时长(秒)")
|
||||
private Integer durationSeconds;
|
||||
|
||||
@Schema(description = "会话时长格式化显示")
|
||||
private String durationFormatted;
|
||||
|
||||
@Schema(description = "消息数量")
|
||||
private Integer messageCount;
|
||||
|
||||
@Schema(description = "会话状态")
|
||||
private String conversationStatus;
|
||||
|
||||
@Schema(description = "会话状态名称")
|
||||
private String conversationStatusName;
|
||||
|
||||
@Schema(description = "会话状态颜色")
|
||||
private String statusColor;
|
||||
|
||||
@Schema(description = "满意度评分(1-5星)")
|
||||
private Integer satisfactionRating;
|
||||
|
||||
@Schema(description = "满意度反馈")
|
||||
private String satisfactionFeedback;
|
||||
|
||||
@Schema(description = "会话摘要")
|
||||
private String summary;
|
||||
|
||||
@Schema(description = "会话标签")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "会话元数据")
|
||||
private JsonNode metadata;
|
||||
|
||||
@Schema(description = "最后一条消息内容")
|
||||
private String lastMessageContent;
|
||||
|
||||
@Schema(description = "最后一条消息时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastMessageTime;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package org.xyzh.api.workcase.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客户信息VO
|
||||
* 用于前端展示客户信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "客户信息VO")
|
||||
public class CustomerVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "客户ID")
|
||||
private String customerId;
|
||||
|
||||
@Schema(description = "客户编号")
|
||||
private String customerNo;
|
||||
|
||||
@Schema(description = "客户姓名")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "客户类型")
|
||||
private String customerType;
|
||||
|
||||
@Schema(description = "客户类型名称")
|
||||
private String customerTypeName;
|
||||
|
||||
@Schema(description = "公司名称")
|
||||
private String companyName;
|
||||
|
||||
@Schema(description = "电话")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "微信OpenID")
|
||||
private String wechatOpenid;
|
||||
|
||||
@Schema(description = "头像URL")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "性别")
|
||||
private Integer gender;
|
||||
|
||||
@Schema(description = "性别名称")
|
||||
private String genderName;
|
||||
|
||||
@Schema(description = "地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "客户等级")
|
||||
private String customerLevel;
|
||||
|
||||
@Schema(description = "客户等级名称")
|
||||
private String customerLevelName;
|
||||
|
||||
@Schema(description = "客户来源")
|
||||
private String customerSource;
|
||||
|
||||
@Schema(description = "客户来源名称")
|
||||
private String customerSourceName;
|
||||
|
||||
@Schema(description = "客户标签")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String notes;
|
||||
|
||||
@Schema(description = "CRM系统客户ID")
|
||||
private String crmCustomerId;
|
||||
|
||||
@Schema(description = "最后联系时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastContactTime;
|
||||
|
||||
@Schema(description = "咨询总次数")
|
||||
private Integer totalConsultations;
|
||||
|
||||
@Schema(description = "订单总数")
|
||||
private Integer totalOrders;
|
||||
|
||||
@Schema(description = "总消费金额")
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
@Schema(description = "满意度评分")
|
||||
private BigDecimal satisfactionScore;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "状态名称")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "状态颜色")
|
||||
private String statusColor;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "更新者姓名")
|
||||
private String updaterName;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.xyzh.api.workcase.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 工单列表VO
|
||||
* 用于前端列表展示(简化版)
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "工单列表VO")
|
||||
public class TicketListVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "工单ID")
|
||||
private String ticketId;
|
||||
|
||||
@Schema(description = "工单编号")
|
||||
private String ticketNo;
|
||||
|
||||
@Schema(description = "客户姓名")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "工单标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "工单类型名称")
|
||||
private String ticketTypeName;
|
||||
|
||||
@Schema(description = "优先级")
|
||||
private String priority;
|
||||
|
||||
@Schema(description = "优先级名称")
|
||||
private String priorityName;
|
||||
|
||||
@Schema(description = "工单状态")
|
||||
private String ticketStatus;
|
||||
|
||||
@Schema(description = "工单状态名称")
|
||||
private String ticketStatusName;
|
||||
|
||||
@Schema(description = "处理人姓名")
|
||||
private String assignedToName;
|
||||
|
||||
@Schema(description = "SLA截止时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date slaDeadline;
|
||||
|
||||
@Schema(description = "是否逾期", defaultValue = "false")
|
||||
private Boolean isOverdue;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package org.xyzh.api.workcase.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.xyzh.common.vo.BaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工单VO
|
||||
* 用于前端展示工单信息
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "工单VO")
|
||||
public class TicketVO extends BaseVO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "工单ID")
|
||||
private String ticketId;
|
||||
|
||||
@Schema(description = "工单编号")
|
||||
private String ticketNo;
|
||||
|
||||
@Schema(description = "客户ID")
|
||||
private String customerId;
|
||||
|
||||
@Schema(description = "客户姓名")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "客户电话")
|
||||
private String customerPhone;
|
||||
|
||||
@Schema(description = "关联会话ID")
|
||||
private String conversationId;
|
||||
|
||||
@Schema(description = "工单类型")
|
||||
private String ticketType;
|
||||
|
||||
@Schema(description = "工单类型名称")
|
||||
private String ticketTypeName;
|
||||
|
||||
@Schema(description = "工单分类")
|
||||
private String ticketCategory;
|
||||
|
||||
@Schema(description = "优先级")
|
||||
private String priority;
|
||||
|
||||
@Schema(description = "优先级名称")
|
||||
private String priorityName;
|
||||
|
||||
@Schema(description = "优先级颜色")
|
||||
private String priorityColor;
|
||||
|
||||
@Schema(description = "工单标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "问题描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "附件ID数组")
|
||||
private List<String> attachments;
|
||||
|
||||
@Schema(description = "附件数量")
|
||||
private Integer attachmentCount;
|
||||
|
||||
@Schema(description = "工单来源")
|
||||
private String ticketSource;
|
||||
|
||||
@Schema(description = "工单来源名称")
|
||||
private String ticketSourceName;
|
||||
|
||||
@Schema(description = "分配给(处理人ID)")
|
||||
private String assignedTo;
|
||||
|
||||
@Schema(description = "处理人姓名")
|
||||
private String assignedToName;
|
||||
|
||||
@Schema(description = "分配部门")
|
||||
private String assignedDept;
|
||||
|
||||
@Schema(description = "分配部门名称")
|
||||
private String assignedDeptName;
|
||||
|
||||
@Schema(description = "工单状态")
|
||||
private String ticketStatus;
|
||||
|
||||
@Schema(description = "工单状态名称")
|
||||
private String ticketStatusName;
|
||||
|
||||
@Schema(description = "工单状态颜色")
|
||||
private String statusColor;
|
||||
|
||||
@Schema(description = "解决方案")
|
||||
private String resolution;
|
||||
|
||||
@Schema(description = "解决时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date resolutionTime;
|
||||
|
||||
@Schema(description = "关闭时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date closeTime;
|
||||
|
||||
@Schema(description = "首次响应时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date responseTime;
|
||||
|
||||
@Schema(description = "SLA截止时间", format = "date-time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date slaDeadline;
|
||||
|
||||
@Schema(description = "是否逾期", defaultValue = "false")
|
||||
private Boolean isOverdue;
|
||||
|
||||
@Schema(description = "距离SLA截止的剩余时间(分钟)")
|
||||
private Integer slaRemainingMinutes;
|
||||
|
||||
@Schema(description = "客户评分(1-5星)")
|
||||
private Integer customerRating;
|
||||
|
||||
@Schema(description = "客户反馈")
|
||||
private String customerFeedback;
|
||||
|
||||
@Schema(description = "CRM系统工单ID")
|
||||
private String crmTicketId;
|
||||
|
||||
@Schema(description = "同步状态")
|
||||
private String syncStatus;
|
||||
|
||||
@Schema(description = "同步状态名称")
|
||||
private String syncStatusName;
|
||||
|
||||
@Schema(description = "工单标签")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "工单元数据")
|
||||
private JsonNode metadata;
|
||||
|
||||
@Schema(description = "处理记录数量")
|
||||
private Integer logCount;
|
||||
|
||||
@Schema(description = "创建者姓名")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "更新者姓名")
|
||||
private String updaterName;
|
||||
}
|
||||
@@ -20,6 +20,11 @@
|
||||
<module>api-message</module>
|
||||
<module>api-log</module>
|
||||
<module>api-system</module>
|
||||
<module>api-crontab</module>
|
||||
<module>api-agent</module>
|
||||
<module>api-bidding</module>
|
||||
<module>api-platform</module>
|
||||
<module>api-workcase</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -23,10 +23,42 @@
|
||||
<groupId>org.xyzh.apis</groupId>
|
||||
<artifactId>api-auth</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh.apis</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh.apis</groupId>
|
||||
<artifactId>api-message</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh.common</groupId>
|
||||
<artifactId>common-auth</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh.common</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh.common</groupId>
|
||||
<artifactId>common-utils</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Cloud Nacos Discovery - 用于 Gateway 路由发现 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-logback-adapter-12</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>logback-adapter</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,433 @@
|
||||
package org.xyzh.auth.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.xyzh.api.auth.service.AuthService;
|
||||
import org.xyzh.api.message.service.MessageService;
|
||||
import org.xyzh.api.system.service.SysUserService;
|
||||
import org.xyzh.api.system.vo.SysUserVO;
|
||||
import org.xyzh.auth.utils.CapcatUtils;
|
||||
import org.xyzh.common.core.domain.LoginParam;
|
||||
import org.xyzh.common.core.domain.LoginDomain;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.dto.sys.TbSysUserDTO;
|
||||
import org.xyzh.common.utils.IDUtils;
|
||||
import org.xyzh.common.utils.validation.method.PhoneValidateMethod;
|
||||
import org.xyzh.common.redis.service.RedisService;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description AuthController.java文件描述 认证控制器
|
||||
* @filename AuthController.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-09-28
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
public class AuthController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
|
||||
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
@DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false)
|
||||
private SysUserService userService;
|
||||
|
||||
@DubboReference(version = "1.0.0", group = "message", timeout = 5000, check = false)
|
||||
private MessageService messageService;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
/**
|
||||
* @description 用户登录
|
||||
* @param loginParam 登录参数
|
||||
* @return ResultDomain<LoginDomain> 登录结果
|
||||
* @author yslg
|
||||
* @since 2025-09-28
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public ResultDomain<LoginDomain> login(@RequestBody LoginParam loginParam, HttpServletRequest request) {
|
||||
return authService.login(loginParam, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 用户退出登录
|
||||
* @param loginDomain 登录域对象
|
||||
* @return ResultDomain<String> 退出结果
|
||||
* @author yslg
|
||||
* @since 2025-09-28
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public ResultDomain<LoginDomain> logout(HttpServletRequest request) {
|
||||
return authService.logout(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取验证码
|
||||
* @return ResultDomain<String> 验证码
|
||||
* @author yslg
|
||||
* @since 2025-09-28
|
||||
*/
|
||||
@GetMapping("/captcha")
|
||||
public ResultDomain<String> getCaptcha() {
|
||||
// TODO: 实现验证码生成逻辑
|
||||
// 生成验证码会话ID,用于验证时匹配
|
||||
String captchaId = IDUtils.generateID();
|
||||
String captchaData = captchaId + ":captcha-placeholder"; // 格式: ID:验证码内容
|
||||
|
||||
return ResultDomain.success("验证码获取成功", captchaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 刷新令牌
|
||||
* @param token 原令牌
|
||||
* @return ResultDomain<String> 新令牌
|
||||
* @author yslg
|
||||
* @since 2025-09-28
|
||||
*/
|
||||
@PostMapping("/refresh")
|
||||
public ResultDomain<String> refreshToken(@RequestHeader("Authorization") String token) {
|
||||
// TODO: 实现令牌刷新逻辑
|
||||
// 为新令牌生成唯一ID
|
||||
String newTokenId = IDUtils.generateID();
|
||||
String newToken = "new-token-" + newTokenId; // 临时占位符
|
||||
|
||||
return ResultDomain.success("令牌刷新成功", newToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 健康检查
|
||||
* @return ResultDomain<String> 健康状态
|
||||
* @author yslg
|
||||
* @since 2025-09-28
|
||||
*/
|
||||
@GetMapping("/health")
|
||||
public ResultDomain<String> health() {
|
||||
return ResultDomain.success("认证服务运行正常", "OK");
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 发送邮箱验证码
|
||||
* @param requestBody 包含email字段的请求体
|
||||
* @return ResultDomain<Boolean> 发送结果
|
||||
* @author yslg
|
||||
* @since 2025-11-03
|
||||
*/
|
||||
@PostMapping("/send-email-code")
|
||||
public ResultDomain<Map<String, String>> sendEmailCode(@RequestBody Map<String, String> requestBody) {
|
||||
String email = requestBody.get("email");
|
||||
|
||||
// 验证邮箱格式
|
||||
if (email == null || email.trim().isEmpty()) {
|
||||
return ResultDomain.failure("邮箱不能为空");
|
||||
}
|
||||
|
||||
String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";
|
||||
if (!email.matches(emailRegex)) {
|
||||
return ResultDomain.failure("邮箱格式不正确");
|
||||
}
|
||||
|
||||
// 检查是否频繁发送(60秒内只能发送一次)
|
||||
String rateLimitKey = "email:code:ratelimit:" + email;
|
||||
if (redisService.hasKey(rateLimitKey)) {
|
||||
return ResultDomain.failure("验证码已发送,请勿重复发送");
|
||||
}
|
||||
|
||||
// 生成会话ID(用于绑定验证码和用户)
|
||||
String sessionId = IDUtils.generateID();
|
||||
|
||||
// 生成6位数字验证码
|
||||
String code = CapcatUtils.generateVerificationCode();
|
||||
|
||||
// 发送邮件
|
||||
boolean success = messageService.sendEmailVerificationCode(email, code).getSuccess();
|
||||
|
||||
if (success) {
|
||||
// 将验证码存储到Redis,绑定sessionId,有效期5分钟
|
||||
String codeKey = "email:code:" + sessionId;
|
||||
String codeValue = email + ":" + code; // 格式:邮箱:验证码
|
||||
redisService.set(codeKey, codeValue, 5, TimeUnit.MINUTES);
|
||||
|
||||
// 设置5分钟的发送频率限制
|
||||
redisService.set(rateLimitKey, "1", 5, TimeUnit.MINUTES);
|
||||
|
||||
// 返回sessionId给前端
|
||||
Map<String, String> data = Map.of(
|
||||
"sessionId", sessionId,
|
||||
"message", "验证码已发送到邮箱"
|
||||
);
|
||||
|
||||
logger.info("邮箱验证码已发送,邮箱: {}, sessionId: {}", email, sessionId);
|
||||
return ResultDomain.success("验证码已发送到邮箱", data);
|
||||
} else {
|
||||
return ResultDomain.failure("验证码发送失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 发送手机验证码
|
||||
* @param requestBody 包含phone字段的请求体
|
||||
* @return ResultDomain<Boolean> 发送结果
|
||||
* @author yslg
|
||||
* @since 2025-11-03
|
||||
*/
|
||||
@PostMapping("/send-sms-code")
|
||||
public ResultDomain<Map<String, String>> sendSmsCode(@RequestBody Map<String, String> requestBody) {
|
||||
String phone = requestBody.get("phone");
|
||||
|
||||
// 验证手机号格式
|
||||
if (phone == null || phone.trim().isEmpty()) {
|
||||
return ResultDomain.failure("手机号不能为空");
|
||||
}
|
||||
PhoneValidateMethod validateMethod = new PhoneValidateMethod();
|
||||
if (!validateMethod.validate(phone)) {
|
||||
return ResultDomain.failure("手机号格式不正确");
|
||||
}
|
||||
|
||||
// 检查是否频繁发送(60秒内只能发送一次)
|
||||
String rateLimitKey = "sms:code:ratelimit:" + phone;
|
||||
if (redisService.hasKey(rateLimitKey)) {
|
||||
return ResultDomain.failure("验证码已发送,请勿重复发送");
|
||||
}
|
||||
|
||||
// 生成会话ID(用于绑定验证码和用户)
|
||||
String sessionId = IDUtils.generateID();
|
||||
|
||||
// 生成6位数字验证码
|
||||
String code = CapcatUtils.generateVerificationCode();
|
||||
|
||||
// 发送短信
|
||||
boolean success = messageService.sendPhoneVerificationCode(phone, code).getSuccess();
|
||||
|
||||
if (success) {
|
||||
// 将验证码存储到Redis,绑定sessionId,有效期5分钟
|
||||
String codeKey = "sms:code:" + sessionId;
|
||||
String codeValue = phone + ":" + code; // 格式:手机号:验证码
|
||||
redisService.set(codeKey, codeValue, 5, TimeUnit.MINUTES);
|
||||
|
||||
// 设置5分钟的发送频率限制
|
||||
redisService.set(rateLimitKey, "1", 5, TimeUnit.MINUTES);
|
||||
|
||||
// 返回sessionId给前端
|
||||
Map<String, String> data = Map.of(
|
||||
"sessionId", sessionId,
|
||||
"message", "验证码已发送"
|
||||
);
|
||||
|
||||
logger.info("短信验证码已发送,手机号: {}, sessionId: {}", phone, sessionId);
|
||||
return ResultDomain.success("验证码已发送", data);
|
||||
} else {
|
||||
return ResultDomain.failure("验证码发送失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 用户注册
|
||||
* @param requestBody 注册参数
|
||||
* @return ResultDomain<LoginDomain> 注册结果
|
||||
* @author yslg
|
||||
* @since 2025-11-03
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
public ResultDomain<LoginDomain> register(@RequestBody Map<String, Object> requestBody, HttpServletRequest request) {
|
||||
try {
|
||||
// 获取注册参数
|
||||
String registerType = (String) requestBody.get("registerType");
|
||||
String username = (String) requestBody.get("username");
|
||||
String phone = (String) requestBody.get("phone");
|
||||
String email = (String) requestBody.get("email");
|
||||
String password = (String) requestBody.get("password");
|
||||
String confirmPassword = (String) requestBody.get("confirmPassword");
|
||||
String smsCode = (String) requestBody.get("smsCode");
|
||||
String emailCode = (String) requestBody.get("emailCode");
|
||||
String smsSessionId = (String) requestBody.get("smsSessionId");
|
||||
String emailSessionId = (String) requestBody.get("emailSessionId");
|
||||
String studentId = (String) requestBody.get("studentId");
|
||||
|
||||
// 1. 参数验证
|
||||
if (password == null || password.trim().isEmpty()) {
|
||||
return ResultDomain.failure("密码不能为空");
|
||||
}
|
||||
|
||||
if (password.length() < 6) {
|
||||
return ResultDomain.failure("密码至少6个字符");
|
||||
}
|
||||
|
||||
if (!password.equals(confirmPassword)) {
|
||||
return ResultDomain.failure("两次输入的密码不一致");
|
||||
}
|
||||
|
||||
// 2. 根据注册类型进行不同的验证
|
||||
SysUserVO user = new SysUserVO();
|
||||
|
||||
switch (registerType) {
|
||||
case "username":
|
||||
// 用户名注册
|
||||
if (username == null || username.trim().isEmpty()) {
|
||||
return ResultDomain.failure("用户名不能为空");
|
||||
}
|
||||
if (username.length() < 3 || username.length() > 20) {
|
||||
return ResultDomain.failure("用户名长度为3-20个字符");
|
||||
}
|
||||
if (!username.matches("^[a-zA-Z0-9_]+$")) {
|
||||
return ResultDomain.failure("用户名只能包含字母、数字和下划线");
|
||||
}
|
||||
user.setUsername(username);
|
||||
logger.info("用户名注册: {}", username);
|
||||
break;
|
||||
|
||||
case "phone":
|
||||
// 手机号注册
|
||||
if (phone == null || phone.trim().isEmpty()) {
|
||||
return ResultDomain.failure("手机号不能为空");
|
||||
}
|
||||
if (!phone.matches("^1[3-9]\\d{9}$")) {
|
||||
return ResultDomain.failure("手机号格式不正确");
|
||||
}
|
||||
if (smsCode == null || smsCode.trim().isEmpty()) {
|
||||
return ResultDomain.failure("请输入手机验证码");
|
||||
}
|
||||
if (smsSessionId == null || smsSessionId.trim().isEmpty()) {
|
||||
return ResultDomain.failure("会话已失效,请重新获取验证码");
|
||||
}
|
||||
|
||||
// 通过sessionId验证手机验证码
|
||||
String smsCodeKey = "sms:code:" + smsSessionId;
|
||||
String storedSmsValue = (String) redisService.get(smsCodeKey);
|
||||
if (storedSmsValue == null) {
|
||||
return ResultDomain.failure("验证码已过期,请重新获取");
|
||||
}
|
||||
|
||||
// 解析存储的值:手机号:验证码
|
||||
String[] smsParts = storedSmsValue.split(":");
|
||||
if (smsParts.length != 2) {
|
||||
return ResultDomain.failure("验证码数据异常");
|
||||
}
|
||||
|
||||
String storedPhone = smsParts[0];
|
||||
String storedSmsCode = smsParts[1];
|
||||
|
||||
// 验证手机号和验证码是否匹配
|
||||
if (!storedPhone.equals(phone)) {
|
||||
logger.warn("手机号注册验证失败,提交手机号: {}, 验证码绑定手机号: {}", phone, storedPhone);
|
||||
return ResultDomain.failure("手机号与验证码不匹配");
|
||||
}
|
||||
if (!storedSmsCode.equals(smsCode)) {
|
||||
return ResultDomain.failure("验证码错误");
|
||||
}
|
||||
|
||||
// 验证码使用后删除
|
||||
redisService.delete(smsCodeKey);
|
||||
|
||||
user.setPhone(phone);
|
||||
user.setUsername(phone); // 使用手机号作为用户名
|
||||
logger.info("手机号注册: {}, sessionId: {}", phone, smsSessionId);
|
||||
break;
|
||||
|
||||
case "email":
|
||||
// 邮箱注册
|
||||
if (email == null || email.trim().isEmpty()) {
|
||||
return ResultDomain.failure("邮箱不能为空");
|
||||
}
|
||||
if (!email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) {
|
||||
return ResultDomain.failure("邮箱格式不正确");
|
||||
}
|
||||
if (emailCode == null || emailCode.trim().isEmpty()) {
|
||||
return ResultDomain.failure("请输入邮箱验证码");
|
||||
}
|
||||
if (emailSessionId == null || emailSessionId.trim().isEmpty()) {
|
||||
return ResultDomain.failure("会话已失效,请重新获取验证码");
|
||||
}
|
||||
|
||||
// 通过sessionId验证邮箱验证码
|
||||
String emailCodeKey = "email:code:" + emailSessionId;
|
||||
String storedEmailValue = (String) redisService.get(emailCodeKey);
|
||||
if (storedEmailValue == null) {
|
||||
return ResultDomain.failure("验证码已过期,请重新获取");
|
||||
}
|
||||
|
||||
// 解析存储的值:邮箱:验证码
|
||||
String[] emailParts = storedEmailValue.split(":");
|
||||
if (emailParts.length != 2) {
|
||||
return ResultDomain.failure("验证码数据异常");
|
||||
}
|
||||
|
||||
String storedEmail = emailParts[0];
|
||||
String storedEmailCode = emailParts[1];
|
||||
|
||||
// 验证邮箱和验证码是否匹配
|
||||
if (!storedEmail.equals(email)) {
|
||||
logger.warn("邮箱注册验证失败,提交邮箱: {}, 验证码绑定邮箱: {}", email, storedEmail);
|
||||
return ResultDomain.failure("邮箱与验证码不匹配");
|
||||
}
|
||||
if (!storedEmailCode.equals(emailCode)) {
|
||||
return ResultDomain.failure("验证码错误");
|
||||
}
|
||||
|
||||
// 验证码使用后删除
|
||||
redisService.delete(emailCodeKey);
|
||||
|
||||
user.setEmail(email);
|
||||
user.setUsername(email.split("@")[0]); // 使用邮箱前缀作为用户名
|
||||
logger.info("邮箱注册: {}, sessionId: {}", email, emailSessionId);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ResultDomain.failure("未知的注册类型");
|
||||
}
|
||||
|
||||
// 3. 设置密码(明文,Service层会加密)
|
||||
user.setPassword(password);
|
||||
|
||||
// 4. 设置用户状态为正常
|
||||
user.setStatus("0");
|
||||
|
||||
// 5. 调用UserService注册用户(Service层会加密密码)
|
||||
ResultDomain<TbSysUserDTO> registerResult = userService.registerUser(user);
|
||||
|
||||
if (!registerResult.getSuccess()) {
|
||||
return ResultDomain.failure(registerResult.getMessage());
|
||||
}
|
||||
|
||||
logger.info("用户注册成功: {}", user.getUsername());
|
||||
|
||||
// 6. 注册成功后自动登录
|
||||
LoginParam loginParam = new LoginParam();
|
||||
loginParam.setUsername(user.getUsername());
|
||||
loginParam.setPassword(password);
|
||||
loginParam.setLoginType("password");
|
||||
|
||||
if (phone != null && !phone.trim().isEmpty()) {
|
||||
loginParam.setPhone(phone);
|
||||
}
|
||||
if (email != null && !email.trim().isEmpty()) {
|
||||
loginParam.setEmail(email);
|
||||
}
|
||||
|
||||
ResultDomain<LoginDomain> loginResult = authService.login(loginParam, request);
|
||||
|
||||
if (loginResult.getSuccess()) {
|
||||
return ResultDomain.success("注册成功", loginResult.getData());
|
||||
} else {
|
||||
// 注册成功但登录失败,返回注册成功信息
|
||||
return ResultDomain.success("注册成功,请登录", loginResult.getData());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("用户注册失败", e);
|
||||
return ResultDomain.failure("注册失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.xyzh.auth.exception;
|
||||
import org.xyzh.common.core.exception.BaseException;
|
||||
|
||||
public class AuthException extends BaseException {
|
||||
|
||||
public AuthException(Integer code, String description, String message){
|
||||
super(code, description, message);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ 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.apache.dubbo.config.annotation.DubboService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -17,6 +17,12 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
* @copyright yslg
|
||||
* @since 2025-11-09
|
||||
*/
|
||||
@DubboService(
|
||||
version = "1.0.0",
|
||||
group = "auth",
|
||||
timeout = 3000,
|
||||
retries = 0
|
||||
)
|
||||
public class AuthServiceImpl implements AuthService{
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.xyzh.auth.strategy;
|
||||
|
||||
import org.xyzh.common.core.domain.LoginParam;
|
||||
import org.xyzh.common.dto.sys.TbSysUserDTO;
|
||||
|
||||
/**
|
||||
* @description LoginStrategy.java文件描述 登录策略接口
|
||||
* @filename LoginStrategy.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
public interface LoginStrategy {
|
||||
|
||||
/**
|
||||
* @description 支持的登录类型
|
||||
* @return String 登录类型
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
String getLoginType();
|
||||
|
||||
/**
|
||||
* @description 验证登录参数
|
||||
* @param loginParam 登录参数
|
||||
* @return boolean 是否有效
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
boolean validate(LoginParam loginParam);
|
||||
|
||||
/**
|
||||
* @description 根据登录参数查找用户
|
||||
* @param loginParam 登录参数
|
||||
* @return TbSysUser 用户对象
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
TbSysUserDTO findUser(LoginParam loginParam);
|
||||
|
||||
/**
|
||||
* @description 验证凭据(密码或验证码)
|
||||
* @param inputCredential 输入凭据(密码或验证码)
|
||||
* @param storedCredential 存储凭据(密码或验证码)
|
||||
* @return boolean 是否匹配
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
boolean verifyCredential(String inputCredential, String storedCredential);
|
||||
|
||||
/**
|
||||
* @description 验证验证码(从Redis获取并验证SessionID)
|
||||
* @param loginParam 登录参数
|
||||
* @return boolean 是否验证成功
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
default boolean verifyCaptchaWithSession(LoginParam loginParam) {
|
||||
// 默认实现:不支持验证码登录
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.xyzh.auth.strategy;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.auth.exception.AuthException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @description LoginStrategyFactory.java文件描述 登录策略工厂
|
||||
* @filename LoginStrategyFactory.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
@Component
|
||||
public class LoginStrategyFactory {
|
||||
|
||||
private final Map<String, LoginStrategy> strategies;
|
||||
|
||||
public LoginStrategyFactory(List<LoginStrategy> loginStrategies) {
|
||||
this.strategies = loginStrategies.stream()
|
||||
.collect(Collectors.toMap(LoginStrategy::getLoginType, Function.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录策略
|
||||
* @param loginType 登录类型
|
||||
* @return LoginStrategy 登录策略
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
public LoginStrategy getStrategy(String loginType) {
|
||||
LoginStrategy strategy = strategies.get(loginType);
|
||||
if (strategy == null) {
|
||||
throw new AuthException(1,"UNSUPPORTED_LOGIN_TYPE", "不支持的登录类型: " + loginType);
|
||||
}
|
||||
return strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取所有支持的登录类型
|
||||
* @return Set<String> 登录类型集合
|
||||
* @author yslg
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
public java.util.Set<String> getSupportedLoginTypes() {
|
||||
return strategies.keySet();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package org.xyzh.auth.strategy.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.xyzh.auth.strategy.LoginStrategy;
|
||||
import org.xyzh.common.core.domain.LoginParam;
|
||||
import org.xyzh.common.dto.sys.TbSysUserDTO;
|
||||
import org.xyzh.api.system.service.SysUserService;
|
||||
import org.xyzh.api.system.vo.SysUserVO;
|
||||
import org.xyzh.common.redis.service.RedisService;
|
||||
|
||||
/**
|
||||
* @description EmailLoginStrategy.java文件描述 邮箱登录策略
|
||||
* @filename EmailLoginStrategy.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
@Component
|
||||
public class EmailLoginStrategy implements LoginStrategy {
|
||||
|
||||
@Autowired
|
||||
private SysUserService userService;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public String getLoginType() {
|
||||
return "email";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(LoginParam loginParam) {
|
||||
if (loginParam.getEmail() == null || loginParam.getEmail().trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 密码登录或验证码登录都可以
|
||||
return (loginParam.getPassword() != null && !loginParam.getPassword().trim().isEmpty())
|
||||
|| (loginParam.getCaptcha() != null && !loginParam.getCaptcha().trim().isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbSysUserDTO findUser(LoginParam loginParam) {
|
||||
TbSysUserDTO filter = new TbSysUserDTO();
|
||||
filter.setEmail(loginParam.getEmail());
|
||||
TbSysUserDTO user = userService.getLoginUser(filter).getData();
|
||||
if(user == null) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
@Override
|
||||
public boolean verifyCredential(String inputCredential, String storedCredential) {
|
||||
// 密码验证
|
||||
return passwordEncoder.matches(inputCredential, storedCredential);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyCaptchaWithSession(LoginParam loginParam) {
|
||||
String captchaId = loginParam.getCaptchaId();
|
||||
String inputCaptcha = loginParam.getCaptcha();
|
||||
String email = loginParam.getEmail();
|
||||
|
||||
// 验证参数
|
||||
if (captchaId == null || captchaId.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (inputCaptcha == null || inputCaptcha.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 从Redis获取验证码
|
||||
String codeKey = "email:code:" + captchaId;
|
||||
String storedValue = (String) redisService.get(codeKey);
|
||||
|
||||
if (storedValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解析存储的值:邮箱:验证码
|
||||
String[] parts = storedValue.split(":");
|
||||
if (parts.length != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String storedEmail = parts[0];
|
||||
String storedCaptcha = parts[1];
|
||||
|
||||
// 验证邮箱和验证码是否匹配
|
||||
if (!storedEmail.equals(email) || !storedCaptcha.equals(inputCaptcha)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证码使用后删除
|
||||
redisService.delete(codeKey);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.xyzh.auth.strategy.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.xyzh.auth.strategy.LoginStrategy;
|
||||
import org.xyzh.common.core.domain.LoginParam;
|
||||
import org.xyzh.common.dto.sys.TbSysUserDTO;
|
||||
import org.xyzh.common.utils.NonUtils;
|
||||
import org.xyzh.common.utils.validation.method.EmailValidateMethod;
|
||||
import org.xyzh.common.utils.validation.method.PhoneValidateMethod;
|
||||
import org.xyzh.api.system.service.SysUserService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @description PasswordLoginStrategy.java文件描述 密码登录策略
|
||||
* @filename PasswordLoginStrategy.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
@Component
|
||||
public class PasswordLoginStrategy implements LoginStrategy {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PasswordLoginStrategy.class);
|
||||
|
||||
@Autowired
|
||||
private SysUserService userService;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public String getLoginType() {
|
||||
return "password";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(LoginParam loginParam) {
|
||||
if (NonUtils.isEmpty(loginParam.getPassword())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NonUtils.isEmpty(loginParam.getUsername()) && NonUtils.isEmpty(loginParam.getEmail()) && NonUtils.isEmpty(loginParam.getPhone())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbSysUserDTO findUser(LoginParam loginParam) {
|
||||
TbSysUserDTO filter = new TbSysUserDTO();
|
||||
EmailValidateMethod emailValidateMethod = new EmailValidateMethod();
|
||||
PhoneValidateMethod phoneValidateMethod = new PhoneValidateMethod();
|
||||
if(emailValidateMethod.validate(loginParam.getUsername())){
|
||||
filter.setEmail(loginParam.getUsername());
|
||||
}else if (phoneValidateMethod.validate(loginParam.getUsername())){
|
||||
filter.setPhone(loginParam.getUsername());
|
||||
}else{
|
||||
filter.setUsername(loginParam.getUsername());
|
||||
}
|
||||
// 【优化】删除无用的密码编码,SQL查询不使用password字段
|
||||
// 密码验证在 verifyCredential() 方法中进行
|
||||
TbSysUserDTO user = userService.getLoginUser(filter).getData();
|
||||
if(user == null) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyCredential(String inputCredential, String storedCredential) {
|
||||
// 使用BCrypt的matches方法验证密码(内部会自动处理salt)
|
||||
return passwordEncoder.matches(inputCredential, storedCredential);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.xyzh.auth.strategy.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.xyzh.auth.strategy.LoginStrategy;
|
||||
import org.xyzh.common.core.domain.LoginParam;
|
||||
import org.xyzh.common.dto.sys.TbSysUserDTO;
|
||||
import org.xyzh.api.system.service.SysUserService;
|
||||
import org.xyzh.common.redis.service.RedisService;
|
||||
|
||||
/**
|
||||
* @description PhoneLoginStrategy.java文件描述 手机号登录策略
|
||||
* @filename PhoneLoginStrategy.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
@Component
|
||||
public class PhoneLoginStrategy implements LoginStrategy {
|
||||
|
||||
@Autowired
|
||||
private SysUserService userService;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public String getLoginType() {
|
||||
return "phone";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(LoginParam loginParam) {
|
||||
if (loginParam.getPhone() == null || loginParam.getPhone().trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 密码登录或验证码登录都可以
|
||||
return (loginParam.getPassword() != null && !loginParam.getPassword().trim().isEmpty())
|
||||
|| (loginParam.getCaptcha() != null && !loginParam.getCaptcha().trim().isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbSysUserDTO findUser(LoginParam loginParam) {
|
||||
TbSysUserDTO filter = new TbSysUserDTO();
|
||||
filter.setPhone(loginParam.getPhone());
|
||||
TbSysUserDTO user = userService.getLoginUser(filter).getData();
|
||||
if(user == null) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
@Override
|
||||
public boolean verifyCredential(String inputCredential, String storedCredential) {
|
||||
// 密码验证
|
||||
return passwordEncoder.matches(inputCredential, storedCredential);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyCaptchaWithSession(LoginParam loginParam) {
|
||||
String captchaId = loginParam.getCaptchaId();
|
||||
String inputCaptcha = loginParam.getCaptcha();
|
||||
String phone = loginParam.getPhone();
|
||||
|
||||
// 验证参数
|
||||
if (captchaId == null || captchaId.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (inputCaptcha == null || inputCaptcha.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 从Redis获取验证码
|
||||
String codeKey = "sms:code:" + captchaId;
|
||||
String storedValue = (String) redisService.get(codeKey);
|
||||
|
||||
if (storedValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解析存储的值:手机号:验证码
|
||||
String[] parts = storedValue.split(":");
|
||||
if (parts.length != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String storedPhone = parts[0];
|
||||
String storedCaptcha = parts[1];
|
||||
|
||||
// 验证手机号和验证码是否匹配
|
||||
if (!storedPhone.equals(phone) || !storedCaptcha.equals(inputCaptcha)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证码使用后删除
|
||||
redisService.delete(codeKey);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.xyzh.auth.strategy.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.xyzh.auth.strategy.LoginStrategy;
|
||||
import org.xyzh.common.core.domain.LoginParam;
|
||||
import org.xyzh.common.dto.sys.TbSysUserDTO;
|
||||
import org.xyzh.api.system.service.SysUserService;
|
||||
|
||||
/**
|
||||
* @description UsernameLoginStrategy.java文件描述 用户名登录策略
|
||||
* @filename UsernameLoginStrategy.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
@Component
|
||||
public class UsernameLoginStrategy implements LoginStrategy {
|
||||
|
||||
@Autowired
|
||||
private SysUserService userService;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public String getLoginType() {
|
||||
return "username";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(LoginParam loginParam) {
|
||||
return loginParam.getUsername() != null && !loginParam.getUsername().trim().isEmpty()
|
||||
&& loginParam.getPassword() != null && !loginParam.getPassword().trim().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbSysUserDTO findUser(LoginParam loginParam) {
|
||||
TbSysUserDTO filter = new TbSysUserDTO();
|
||||
filter.setUsername(loginParam.getUsername());
|
||||
TbSysUserDTO user = userService.getLoginUser(filter).getData();
|
||||
if(user == null) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyCredential(String inputCredential, String storedCredential) {
|
||||
return passwordEncoder.matches(inputCredential, storedCredential);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.xyzh.auth.strategy.impl;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.xyzh.auth.strategy.LoginStrategy;
|
||||
import org.xyzh.common.core.domain.LoginParam;
|
||||
import org.xyzh.common.dto.sys.TbSysUserDTO;
|
||||
import org.xyzh.api.system.service.SysUserService;
|
||||
|
||||
/**
|
||||
* @description WechatLoginStrategy.java文件描述 微信登录策略
|
||||
* @filename WechatLoginStrategy.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
@Component
|
||||
public class WechatLoginStrategy implements LoginStrategy {
|
||||
|
||||
@Autowired
|
||||
private SysUserService userService;
|
||||
|
||||
|
||||
@Override
|
||||
public String getLoginType() {
|
||||
return "wechat";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(LoginParam loginParam) {
|
||||
return loginParam.getWechatId() != null && !loginParam.getWechatId().trim().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbSysUserDTO findUser(LoginParam loginParam) {
|
||||
TbSysUserDTO filter = new TbSysUserDTO();
|
||||
filter.setWechatId(loginParam.getWechatId());
|
||||
TbSysUserDTO user = userService.getLoginUser(filter).getData();
|
||||
if(user == null) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyCredential(String inputCredential, String storedCredential) {
|
||||
// 微信登录通常不需要密码验证,通过微信授权码验证
|
||||
// 这里可以添加微信授权验证逻辑
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.xyzh.auth.utils;
|
||||
|
||||
public class CapcatUtils {
|
||||
/**
|
||||
* 生成6位数字验证码
|
||||
* @return 验证码
|
||||
*/
|
||||
public static String generateVerificationCode() {
|
||||
return String.valueOf((int)((Math.random() * 9 + 1) * 100000));
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,94 @@
|
||||
# ================== Server ==================
|
||||
server:
|
||||
port: 8081
|
||||
port: 8181
|
||||
servlet:
|
||||
context-path: /urban-lifeline/auth
|
||||
|
||||
# ================== Auth ====================
|
||||
urban-lifeline:
|
||||
auth:
|
||||
enabled: false # 认证服务自己不需要认证
|
||||
whitelist:
|
||||
- /** # 认证服务的所有接口都放行
|
||||
security:
|
||||
aes:
|
||||
secret-key: 1234567890qwer
|
||||
# ================== Spring ==================
|
||||
spring:
|
||||
application:
|
||||
name: auth-service
|
||||
|
||||
# ================== Spring Cloud Nacos ==================
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
namespace: dev
|
||||
group: DEFAULT_GROUP
|
||||
|
||||
# ================== DataSource ==================
|
||||
datasource:
|
||||
url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline
|
||||
username: postgres
|
||||
password: postgres
|
||||
driver-class-name: org.postgresql.Driver
|
||||
|
||||
# ================== Redis ==================
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改
|
||||
port: 6379
|
||||
database: 0
|
||||
password: 123456 # 如果有密码就填上,没密码可以去掉这一行
|
||||
|
||||
# ================== SpringDoc ==================
|
||||
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: '/**'
|
||||
|
||||
# ================== Dubbo + Nacos ==================
|
||||
dubbo:
|
||||
application:
|
||||
name: urban-lifeline-auth
|
||||
qos-enable: false
|
||||
protocol:
|
||||
name: dubbo
|
||||
port: -1
|
||||
registry:
|
||||
address: nacos://127.0.0.1:8848
|
||||
scan:
|
||||
base-packages: org.xyzh.auth.service.impl
|
||||
|
||||
# ================== MyBatis ==================
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath:mapper/**/*.xml
|
||||
type-aliases-package: org.xyzh.common.dto, org.xyzh.api
|
||||
|
||||
# ================== JWT Configuration ==================
|
||||
jwt:
|
||||
secret: urban-lifeline-secret-key-2025-xyzh
|
||||
expiration: 86400 # 24小时(秒)
|
||||
refresh-expiration: 604800 # 7天(秒)
|
||||
|
||||
# ================== Logging ==================
|
||||
logging:
|
||||
config: classpath:log4j2.xml
|
||||
charset:
|
||||
console: UTF-8
|
||||
file: UTF-8
|
||||
|
||||
|
||||
94
urbanLifelineServ/auth/src/main/resources/log4j2.xml
Normal file
94
urbanLifelineServ/auth/src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration status="WARN" monitorInterval="30">
|
||||
<Properties>
|
||||
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
|
||||
<property name="FILE_PATH" value="./logs" />
|
||||
<property name="FILE_NAME" value="auth-service" />
|
||||
<property name="file.encoding" value="UTF-8" />
|
||||
<property name="console.encoding" value="UTF-8" />
|
||||
</Properties>
|
||||
|
||||
<appenders>
|
||||
<!-- 控制台输出 -->
|
||||
<console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</console>
|
||||
|
||||
<!-- INFO级别日志 -->
|
||||
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/${FILE_NAME}-info.log"
|
||||
filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="100MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="30"/>
|
||||
</RollingFile>
|
||||
|
||||
<!-- WARN级别日志 -->
|
||||
<RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/${FILE_NAME}-warn.log"
|
||||
filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="100MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="30"/>
|
||||
</RollingFile>
|
||||
|
||||
<!-- ERROR级别日志 -->
|
||||
<RollingFile name="RollingFileError" fileName="${FILE_PATH}/${FILE_NAME}-error.log"
|
||||
filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="100MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="30"/>
|
||||
</RollingFile>
|
||||
</appenders>
|
||||
|
||||
<loggers>
|
||||
<!-- Nacos 日志 -->
|
||||
<logger name="com.alibaba.nacos" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</logger>
|
||||
|
||||
<!-- MyBatis 日志 -->
|
||||
<logger name="org.mybatis" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</logger>
|
||||
|
||||
<!-- Spring 日志 -->
|
||||
<Logger name="org.springframework" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
|
||||
<!-- Auth 模块日志 -->
|
||||
<Logger name="org.xyzh.auth" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
</Logger>
|
||||
|
||||
<!-- 项目通用模块日志 -->
|
||||
<Logger name="org.xyzh.common" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
</Logger>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="Console"/>
|
||||
<appender-ref ref="RollingFileInfo"/>
|
||||
<appender-ref ref="RollingFileWarn"/>
|
||||
<appender-ref ref="RollingFileError"/>
|
||||
</root>
|
||||
</loggers>
|
||||
</configuration>
|
||||
21
urbanLifelineServ/bidding/pom.xml
Normal file
21
urbanLifelineServ/bidding/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>urban-lifeline</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>bidding</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.xyzh.bidding;
|
||||
|
||||
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.bidding", // 当前bidding模块
|
||||
"org.xyzh.common" // 公共模块
|
||||
})
|
||||
public class BiddingApp {
|
||||
private static final Logger logger = LoggerFactory.getLogger(BiddingApp.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
logger.info("======================== BiddingApp 启动中 =========================");
|
||||
SpringApplication.run(BiddingApp.class, args);
|
||||
logger.info("======================== BiddingApp 启动成功 =========================");
|
||||
}
|
||||
}
|
||||
89
urbanLifelineServ/bidding/src/main/resources/application.yml
Normal file
89
urbanLifelineServ/bidding/src/main/resources/application.yml
Normal file
@@ -0,0 +1,89 @@
|
||||
# ================== Server ==================
|
||||
server:
|
||||
port: 8186
|
||||
servlet:
|
||||
context-path: /urban-lifeline/bidding
|
||||
|
||||
# ================== Auth ====================
|
||||
urban-lifeline:
|
||||
auth:
|
||||
enabled: true
|
||||
whitelist:
|
||||
- /swagger-ui/**
|
||||
- /swagger-ui.html
|
||||
- /v3/api-docs/**
|
||||
- /webjars/**
|
||||
- /favicon.ico
|
||||
- /error
|
||||
- /actuator/health
|
||||
- /actuator/info
|
||||
|
||||
security:
|
||||
aes:
|
||||
secret-key: 1234567890qwer
|
||||
|
||||
# ================== Spring ==================
|
||||
spring:
|
||||
application:
|
||||
name: bidding-service
|
||||
|
||||
# ================== Spring Cloud Nacos ==================
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
namespace: dev
|
||||
group: DEFAULT_GROUP
|
||||
|
||||
# ================== DataSource ==================
|
||||
datasource:
|
||||
url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline
|
||||
username: postgres
|
||||
password: postgres
|
||||
driver-class-name: org.postgresql.Driver
|
||||
|
||||
# ================== Redis ==================
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改
|
||||
port: 6379
|
||||
database: 0
|
||||
password: 123456 # 如果有密码就填上,没密码可以去掉这一行
|
||||
|
||||
# ================== SpringDoc ==================
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: true
|
||||
path: /v3/api-docs
|
||||
swagger-ui:
|
||||
enabled: true
|
||||
path: /swagger-ui.html
|
||||
group-configs:
|
||||
- group: 'default'
|
||||
display-name: '招投标服务 API'
|
||||
paths-to-match: '/**'
|
||||
|
||||
# ================== Dubbo + Nacos ==================
|
||||
dubbo:
|
||||
application:
|
||||
name: urban-lifeline-bidding
|
||||
qos-enable: false
|
||||
protocol:
|
||||
name: dubbo
|
||||
port: -1
|
||||
registry:
|
||||
address: nacos://127.0.0.1:8848
|
||||
scan:
|
||||
base-packages: org.xyzh.bidding.service.impl
|
||||
|
||||
# ================== MyBatis ==================
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath:mapper/**/*.xml
|
||||
type-aliases-package: org.xyzh.common.dto, org.xyzh.api
|
||||
|
||||
# ================== Logging ==================
|
||||
logging:
|
||||
config: classpath:log4j2.xml
|
||||
charset:
|
||||
console: UTF-8
|
||||
file: UTF-8
|
||||
85
urbanLifelineServ/bidding/src/main/resources/log4j2.xml
Normal file
85
urbanLifelineServ/bidding/src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration status="WARN" monitorInterval="30">
|
||||
<Properties>
|
||||
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
|
||||
<property name="FILE_PATH" value="./logs" />
|
||||
<property name="FILE_NAME" value="bidding-service" />
|
||||
<property name="file.encoding" value="UTF-8" />
|
||||
<property name="console.encoding" value="UTF-8" />
|
||||
</Properties>
|
||||
|
||||
<appenders>
|
||||
<console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</console>
|
||||
|
||||
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/${FILE_NAME}-info.log"
|
||||
filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="100MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="30"/>
|
||||
</RollingFile>
|
||||
|
||||
<RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/${FILE_NAME}-warn.log"
|
||||
filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="100MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="30"/>
|
||||
</RollingFile>
|
||||
|
||||
<RollingFile name="RollingFileError" fileName="${FILE_PATH}/${FILE_NAME}-error.log"
|
||||
filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="100MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="30"/>
|
||||
</RollingFile>
|
||||
</appenders>
|
||||
|
||||
<loggers>
|
||||
<logger name="com.alibaba.nacos" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.mybatis" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</logger>
|
||||
|
||||
<Logger name="org.springframework" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
|
||||
<Logger name="org.xyzh.bidding" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
</Logger>
|
||||
|
||||
<Logger name="org.xyzh.common" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
</Logger>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="Console"/>
|
||||
<appender-ref ref="RollingFileInfo"/>
|
||||
<appender-ref ref="RollingFileWarn"/>
|
||||
<appender-ref ref="RollingFileError"/>
|
||||
</root>
|
||||
</loggers>
|
||||
</configuration>
|
||||
@@ -10,8 +10,8 @@ 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.redis.service.RedisService;
|
||||
import org.xyzh.common.utils.NonUtils;
|
||||
import org.xyzh.redis.service.RedisService;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.List;
|
||||
* @author yslg
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "urban-lifeline.auth")
|
||||
@ConfigurationProperties(prefix = "auth")
|
||||
public class AuthProperties {
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,16 +7,26 @@ 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.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
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;
|
||||
import org.xyzh.common.redis.service.RedisService;
|
||||
|
||||
@Configuration
|
||||
@EnableMethodSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
/**
|
||||
* 密码编码器 - 用于用户密码加密(使用 BCrypt)
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT 认证过滤器 - 仅在非 Gateway 模式下启用
|
||||
* 当 auth.gateway-mode=false 或未配置时使用
|
||||
|
||||
@@ -20,7 +20,7 @@ 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.xyzh.common.redis.service.RedisService;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
@@ -23,10 +23,10 @@ import java.util.Map;
|
||||
@Component
|
||||
public class JwtTokenUtil {
|
||||
|
||||
@Value("${urban-lifeline.auth.jwt-secret:schoolNewsDefaultSecretKeyForJWT2025}")
|
||||
@Value("${auth.jwt-secret:urbanLifelineDefaultSecretKeyForJWT2025}")
|
||||
private String secret;
|
||||
|
||||
@Value("${urban-lifeline.auth.jwt-expiration:86400}")
|
||||
@Value("${auth.jwt-expiration:86400}")
|
||||
private Long expiration;
|
||||
|
||||
private SecretKey getSigningKey() {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.xyzh.common.core.exception;
|
||||
|
||||
public class BaseException extends RuntimeException{
|
||||
private Integer code;
|
||||
|
||||
private String description;
|
||||
|
||||
public BaseException(Integer code, String description, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Integer getCode(){
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public String getDescription(){
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public String getMessage(){
|
||||
return super.getMessage();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -29,9 +29,12 @@ public class TbSysUserDTO extends BaseDTO {
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "手机")
|
||||
@Schema(description = "手机(加密)")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "手机号哈希")
|
||||
private String phone_hash;
|
||||
|
||||
@Schema(description = "微信ID")
|
||||
private String wechatId;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.xyzh.redis.config;
|
||||
package org.xyzh.common.redis.config;
|
||||
|
||||
import org.xyzh.common.core.constant.Constants;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.xyzh.redis.config;
|
||||
package org.xyzh.common.redis.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.xyzh.redis.service;
|
||||
package org.xyzh.common.redis.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
@@ -13,7 +13,6 @@
|
||||
<artifactId>common-utils</artifactId>
|
||||
<version>${urban-lifeline.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
@@ -30,5 +29,36 @@
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis (用于类型处理器) -->
|
||||
<dependency>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot (用于加密工具) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Spring Mail for Email -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Aliyun SMS Service -->
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>dysmsapi20170525</artifactId>
|
||||
<version>4.2.0</version>
|
||||
</dependency>
|
||||
<!-- FastJson2 Spring6 支持 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2-extension-spring6</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user