diff --git a/docs/superpowers/plans/2026-04-10-search-plan.md b/docs/superpowers/plans/2026-04-10-search-plan.md index 4156ede..a3c9378 100644 --- a/docs/superpowers/plans/2026-04-10-search-plan.md +++ b/docs/superpowers/plans/2026-04-10-search-plan.md @@ -244,26 +244,33 @@ public class SearchServiceImpl implements SearchService { CommonResult>> result = zxxxbService.zxxx(); List resultList = new ArrayList<>(); + String categoryType = reqVO.getCategoryType(); if (result != null && result.getData() != null) { java.util.Map> data = result.getData(); // 从资讯数据中筛选 data.forEach((category, list) -> { + String currentCategoryType = getCategoryType(category); + // 如果选择了分类且不匹配,跳过 + if (!"all".equals(categoryType) && !categoryType.equals(currentCategoryType)) { + return; + } + list.forEach(zxxx -> { // 简单匹配逻辑,实际应更复杂 - if (matchKeyword(zxxx.getTitle(), reqVO.getKeyword()) || - matchKeyword(zxxx.getContent(), reqVO.getKeyword())) { + if (matchKeyword(zxxx.getBt1(), reqVO.getKeyword()) || + matchKeyword(zxxx.getZxNr(), reqVO.getKeyword())) { SearchResultVO vo = new SearchResultVO(); vo.setId(zxxx.getId()); - vo.setTitle(highlightKeyword(zxxx.getTitle(), reqVO.getKeyword())); - vo.setSummary(highlightKeyword(truncate(zxxx.getContent(), 200), reqVO.getKeyword())); + vo.setTitle(highlightKeyword(zxxx.getBt1(), reqVO.getKeyword())); + vo.setSummary(highlightKeyword(truncate(zxxx.getZxNr(), 200), reqVO.getKeyword())); vo.setCategory(category); - vo.setCategoryType(getCategoryType(category)); + vo.setCategoryType(currentCategoryType); vo.setSource("碳信网"); vo.setSourceType("news"); - vo.setPublishTime(zxxx.getPublishTime()); + vo.setPublishTime(zxxx.getFbsj()); vo.setUrl("/mhzc/news/" + zxxx.getId()); resultList.add(vo); @@ -1191,7 +1198,76 @@ git commit -m "feat(search): 添加搜索结果项组件" --- -### Task 11: 创建搜索结果列表组件 +### Task 11: 创建空状态组件 + +**Files:** +- Create: `txw-mhzc-web/src/pages/index/components/search/SearchEmpty.vue` + +- [ ] **Step 1: 创建 SearchEmpty.vue** + +```vue + + + + + +``` + +- [ ] **Step 2: 提交代码** + +```bash +git add txw-mhzc-web/src/pages/index/components/search/SearchEmpty.vue +git commit -m "feat(search): 添加空状态组件" +``` + +--- + +### Task 12: 创建搜索结果列表组件 **Files:** - Create: `txw-mhzc-web/src/pages/index/components/search/SearchResultList.vue` @@ -1314,76 +1390,7 @@ git commit -m "feat(search): 添加搜索结果列表组件" --- -### Task 12: 创建空状态组件 - -**Files:** -- Create: `txw-mhzc-web/src/pages/index/components/search/SearchEmpty.vue` - -- [ ] **Step 1: 创建 SearchEmpty.vue** - -```vue - - - - - -``` - -- [ ] **Step 2: 提交代码** - -```bash -git add txw-mhzc-web/src/pages/index/components/search/SearchEmpty.vue -git commit -m "feat(search): 添加空状态组件" -``` - ---- - -### Task 13: 创建搜索结果页 +### Task 14: 创建搜索结果页 **Files:** - Create: `txw-mhzc-web/src/pages/index/views/search/index.vue` @@ -1611,7 +1618,7 @@ git commit -m "feat(search): 添加搜索结果页" --- -### Task 14: 添加搜索路由 +### Task 15: 添加搜索路由 **Files:** - Modify: `txw-mhzc-web/src/pages/index/router/routes.js` @@ -1655,7 +1662,7 @@ git commit -m "feat(search): 添加搜索路由" ## 联调与测试 -### Task 15: 前后端联调 +### Task 16: 前后端联调 - [ ] **Step 1: 验证后端接口** diff --git a/docs/superpowers/plans/2026-04-22-gxzx-migration-plan.md b/docs/superpowers/plans/2026-04-22-gxzx-migration-plan.md new file mode 100644 index 0000000..5b7e8ff --- /dev/null +++ b/docs/superpowers/plans/2026-04-22-gxzx-migration-plan.md @@ -0,0 +1,319 @@ +# gxzx 服务迁移至 mhzc 方案 + +## 一、迁移背景 + +`gxzx`(供需大厅/绿色金融/绿色交易/企业入驻)需要合并到 `mhzc` 服务中,以实现: +1. 统一服务治理,减少跨服务调用 +2. 支撑服务中心的搜索聚合功能 +3. 简化系统架构 + +--- + +## 二、迁移范围 + +### 2.1 数据库表(9张) + +| 序号 | 表名 | 说明 | +|------|------|------| +| 1 | `txw_gxzx_gxxxb` | 供需信息表 | +| 2 | `txw_gxzx_shqkb` | 审核情况表 | +| 3 | `txw_gxzx_gxscb` | 供需收藏表 | +| 4 | `txw_gxzx_gxbqb` | 供需标签表 | +| 5 | `txw_gxzx_qybqb` | 企业标签表 | +| 6 | `txw_gxzx_rzsqjlb` | 入驻申请记录表 | +| 7 | `txw_gxzx_lsjrcpxx` | 绿色金融产品信息表 | +| 8 | `txw_gxzx_dkbxsqxx` | 贷款保险申请信息表 | +| 9 | `txw_gxzx_lsjy_zcxx` | 绿色交易资产信息表 | + +### 2.2 Java代码 + +| 层次 | 文件数 | 说明 | +|------|--------|------| +| Controller | 4 | GxdtController, LsjrController, LsjyController, QyRzController | +| Service接口 | 8 | 含业务接口定义 | +| Service实现 | 7 | 业务逻辑实现 | +| Mapper | 9 | MyBatis Mapper接口 | +| Mapper XML | 9 | MyBatis XML映射文件 | +| Domain | 9 | 数据实体DO | +| VO/Req/Res | ~20 | 传输对象 | + +### 2.3 核心业务功能 + +1. **供需大厅**(GxdtController)- 供需发布/审批/上下架/收藏 +2. **绿色金融**(LsjrController)- 信贷/保险产品管理 +3. **绿色交易**(LsjyController)- 资产信息管理 +4. **企业入驻**(QyRzController)- 入驻申请审批 + +--- + +## 三、迁移步骤 + +### 阶段一:数据库迁移 + +1. **备份源库**:在迁移前备份 `txw-gxzx` 相关表数据 +2. **创建目标表**:在 `mhzc` 数据库中创建9张表(可保持原表名或重命名) +3. **数据同步**:将数据从 `gxzx` 库同步到 `mhzc` 库 + +```sql +-- 示例:在mhzc库执行(假设两库在同一实例) +INSERT INTO mhzc_db.txw_gxzx_gxxxb SELECT * FROM gxzx_db.txw_gxzx_gxxxb; +``` + +### 阶段二:代码迁移 + +#### 2.1 创建目录结构 + +在 `txw-mhzc` 项目中创建以下目录: + +``` +txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/ +├── controller/gxzx/ # 原 gxzx/controller +├── service/gxzx/ # 原 gxzx/service +│ └── impl/ # 原 gxzx/service/impl +├── mapper/gxzx/ # 原 gxzx/mapper +├── pojo/domain/gxzx/ # 原 gxzx/pojo/domain +├── pojo/vo/gxzx/ # 原 gxzx/pojo/vo +├── pojo/req/gxzx/ # 新建请求对象 +└── pojo/res/gxzx/ # 新建响应对象 +``` + +#### 2.2 迁移文件清单 + +**Controller 层(4个):** +``` +gxzx/controller/GxdtController.java → mhzc/controller/gxzx/GxdtController.java +gxzx/controller/LsjrController.java → mhzc/controller/gxzx/LsjrController.java +gxzx/controller/LsjyController.java → mhzc/controller/gxzx/LsjyController.java +gxzx/controller/QyRzController.java → mhzc/controller/gxzx/QyRzController.java +``` + +**Service 层(8个接口 + 7个实现):** +``` +gxzx/service/TxwGxzxGxxxbService.java → mhzc/service/gxzx/TxwGxzxGxxxbService.java +gxzx/service/TxwGxzxShqkbService.java → mhzc/service/gxzx/TxwGxzxShqkbService.java +gxzx/service/TxwGxzxGxscbService.java → mhzc/service/gxzx/TxwGxzxGxscbService.java +gxzx/service/TxwGxzxGxbqbService.java → mhzc/service/gxzx/TxwGxzxGxbqbService.java +gxzx/service/TxwGxzxQybqbService.java → mhzc/service/gxzx/TxwGxzxQybqbService.java +gxzx/service/TxwGxzxRzsqjlbService.java → mhzc/service/gxzx/TxwGxzxRzsqjlbService.java +gxzx/service/GxzxLsjrService.java → mhzc/service/gxzx/GxzxLsjrService.java +gxzx/service/GxzxLsjyZcxxService.java → mhzc/service/gxzx/GxzxLsjyZcxxService.java + +gxzx/service/impl/... → mhzc/service/gxzx/impl/... +``` + +**Mapper 层(9个):** +``` +gxzx/mapper/TxwGxzxGxxxbMapper.java → mhzc/mapper/gxzx/TxwGxzxGxxxbMapper.java +gxzx/mapper/TxwGxzxShqkbMapper.java → mhzc/mapper/gxzx/TxwGxzxShqkbMapper.java +...(其他7个同理) +``` + +**Domain 层(9个):** +``` +gxzx/pojo/domain/TxwGxzxGxxxbDO.java → mhzc/pojo/domain/gxzx/TxwGxzxGxxxbDO.java +...(其他8个同理) +``` + +**VO/Req/Res 层(~20个):** +``` +gxzx/pojo/vo/*.java → mhzc/pojo/vo/gxzx/ +gxzx/pojo/req/*.java → mhzc/pojo/req/gxzx/ +gxzx/pojo/lsjr/*.java → mhzc/pojo/lsjr/gxzx/ +gxzx/pojo/lsjy/*.java → mhzc/pojo/lsjy/gxzx/ +``` + +**Mapper XML(9个):** +``` +gxzx/src/main/resources/mapper/*.xml → mhzc/src/main/resources/mapper/gxzx/ +``` + +#### 2.3 代码修改要点 + +**1. 包名修改** +```java +// 原 +package com.css.txw.gxzx.controller; +package com.css.txw.gxzx.service; +package com.css.txw.gxzx.mapper; +package com.css.txw.gxzx.pojo.domain; + +// 改为 +package com.css.txw.mhzc.controller.gxzx; +package com.css.txw.mhzc.service.gxzx; +package com.css.txw.mhzc.mapper.gxzx; +package com.css.txw.mhzc.pojo.domain.gxzx; +``` + +**2. import 语句修改** +```java +// 所有 import com.css.txw.gxzx.* 改为 com.css.txw.mhzc.gxzx.* +``` + +**3. Controller @RequestMapping 路径调整** +```java +// 原 +@RequestMapping("/gxdt") +@RequestMapping("/lsjr") +@RequestMapping("/lsjy") +@RequestMapping("/qyrz") + +// 保持不变(前端已对接)或调整 +``` + +**4. ServiceImpl 类注解修改** +```java +// 原 +@Service +public class TxwGxzxGxxxbServiceImpl extends ServiceImpl + +// 改为 +@Service +public class TxwGxzxGxxxbServiceImpl extends ServiceImpl +``` + +**5. MapperScan 配置** +在 `MhzcServiceConfiguration.java` 或新建 `GxzxMapperScanConfiguration.java`: +```java +@MapperScan({"com.css.txw.mhzc.mapper", "com.css.txw.mhzc.mapper.gxzx"}) +``` + +--- + +### 阶段三:依赖调整 + +#### 3.1 移除 gxzx 依赖 + +从 `txw-mhzc/pom.xml` 中移除对 `txw-gxzx-service-api` 的依赖(如果存在)。 + +#### 3.2 保留必要依赖 + +确保以下依赖存在: +```xml + +ggzc-framework-starter-xxzx-api + + +DmJdbcDriver18 + + +txw-common +``` + +#### 3.3 处理外部服务调用 + +**IMhzcApi 调用处理**(原 gxzx 调用 mhzc): +- `TxwGxzxRzsqjlbServiceImpl` 中注入了 `IMhzcApi` +- 迁移后改为直接调用本地方法(企业入驻逻辑已在 mhzc 中) + +**XxzxApi 调用处理**: +- 确保 `txw-mhzc` 有消息中心依赖 +- 如需迁移,保持调用方式不变 + +--- + +### 阶段四:配置调整 + +#### 4.1 网关路由(如有) + +如果使用了网关,需要将 gxzx 相关路由从 gxzx 服务指向 mhzc: + +```yaml +# 网关配置 +- id: gxzx-route + uri: http://mhzc-service + predicates: + - Path=/gxdt/** + - Path=/lsjr/** + - Path=/lsjy/** + - Path=/qyrz/** +``` + +#### 4.2 Nacos 注册 + +确保 `txw-mhzc` 注册到 Nacos,且 gxzx 相关接口可访问。 + +--- + +### 阶段五:前端适配 + +前端 `txw-mhzc-web` 中已有 gxzx 相关接口调用(见 `fwsc/index.js`): + +```javascript +// 当前调用路径(需确认) +/gxzx/gxdt/gxxxList → /mhzc/gxdt/gxxxList 或保持不变 +/gxzx/lsjr/queryJgList → /mhzc/lsjr/queryJgList 或保持不变 +``` + +**方案**:迁移后保持接口路径不变,前端无需修改。 + +--- + +## 四、数据库表迁移脚本 + +```sql +-- 1. 备份原表(可选) +CREATE TABLE txw_gxzx_gxxxb_bak AS SELECT * FROM txw_gxzx_gxxxb; + +-- 2. 创建新表(在 mhzc 库执行) +CREATE TABLE txw_gxzx_gxxxb ( + gx_uuid VARCHAR(64) PRIMARY KEY, + bt_1 VARCHAR(200), + fwlx_dm VARCHAR(20), + sshy VARCHAR(50), + fwfw VARCHAR(500), + fwnr TEXT, + zt VARCHAR(10), + qyuuid VARCHAR(64), + sjzt VARCHAR(10), + gjjg DECIMAL(18,2), + -- 其他字段... +); + +-- 3. 数据迁移 +INSERT INTO mhzc_db.txw_gxzx_gxxxb SELECT * FROM gxzx_db.txw_gxzx_gxxxb; + +-- 4. 验证 +SELECT COUNT(*) FROM txw_gxzx_gxxxb; +``` + +--- + +## 五、风险点与注意事项 + +### 5.1 数据一致性 +- 迁移期间禁止在 gxzx 写入数据 +- 迁移后需验证数据完整性 + +### 5.2 接口兼容性 +- 迁移前后接口路径尽量保持一致 +- 如有变化需同步通知前端 + +### 5.3 事务处理 +- 跨库迁移时可考虑使用分布式事务或分批迁移 +- 确保关键业务(供需发布、审批)的事务完整性 + +### 5.4 依赖服务 +- 消息中心(XxzxApi)需确保可用 +- 确认 IMhzcApi 在 mhzc 中仍有调用需求 + +--- + +## 六、迁移验证清单 + +| 序号 | 验证项 | 方法 | +|------|--------|------| +| 1 | 数据库表迁移完整 | 对比记录数 | +| 2 | Controller 注入正常 | 启动应用无报错 | +| 3 | Service 层无异常 | 单元测试 | +| 4 | Mapper XML 路径正确 | 查询功能正常 | +| 5 | 接口路径可访问 | Postman 测试 | +| 6 | 搜索服务聚合供需数据 | 调用搜索接口验证 | +| 7 | 前端功能正常 | UI 测试 | + +--- + +## 七、后续工作 + +1. **旧服务下线**:gxzx 服务相关接口迁移完成后,可逐步停用 gxzx 服务 +2. **搜索增强**:基于迁移后的供需数据,完善搜索服务聚合逻辑 +3. **代码清理**:移除 gxzx 项目中已迁移的代码 \ No newline at end of file diff --git a/txw-mhzc-web/src/core/request.js b/txw-mhzc-web/src/core/request.js index fe908d6..a1a7c76 100644 --- a/txw-mhzc-web/src/core/request.js +++ b/txw-mhzc-web/src/core/request.js @@ -43,17 +43,14 @@ request.interceptors.request.use( if (newConf.loading) { SingleLoading.startLoading(); } - // 设置随机数, 定位后端日志和解决浏览器缓存 const { url } = newConf; - if (url.indexOf('?') !== -1) { - newConf.url = `${url}&t=${new Date().getTime()}`; // 请求添加时间戳 - } else { - newConf.url = `${url}?t=${new Date().getTime()}`; - } - - // get请求映射params参数 if (newConf.method === 'get' && newConf.params) { - let url = `${newConf.url}?`; + // 先加时间戳(如果 URL 还没参数) + if (url.indexOf('?') === -1) { + newConf.url = `${newConf.url}?t=${new Date().getTime()}&`; + } + // 再追加 params + let paramsUrl = `${newConf.url}`; for (const propName of Object.keys(newConf.params)) { const value = newConf.params[propName]; const part = `${encodeURIComponent(propName)}=`; @@ -62,16 +59,23 @@ request.interceptors.request.use( for (const key of Object.keys(value)) { const params = `${propName}[${key}]`; const subPart = `${encodeURIComponent(params)}=`; - url += `${subPart + encodeURIComponent(value[key])}&`; + paramsUrl += `${subPart + encodeURIComponent(value[key])}&`; } } else { - url += `${part + encodeURIComponent(value)}&`; + paramsUrl += `${part + encodeURIComponent(value)}&`; } } } - url = url.slice(0, -1); + paramsUrl = paramsUrl.slice(0, -1); newConf.params = {}; - newConf.url = url; + newConf.url = paramsUrl; + } else { + // 无 params 时,直接加时间戳 + if (url.indexOf('?') !== -1) { + newConf.url = `${url}&t=${new Date().getTime()}`; + } else { + newConf.url = `${url}?t=${new Date().getTime()}`; + } } return newConf; }, diff --git a/txw-mhzc-web/src/pages/index/api/search.js b/txw-mhzc-web/src/pages/index/api/search.js new file mode 100644 index 0000000..206bc3b --- /dev/null +++ b/txw-mhzc-web/src/pages/index/api/search.js @@ -0,0 +1,64 @@ +import { fetchSso } from '@/core/request'; + +const basurl = '/mhzc'; + +export default { + /** + * 搜索 + * @param {Object} params - { keyword, categoryType, page, pageSize } + */ + search(params) { + return fetchSso({ + url: `${basurl}/search`, + method: 'get', + loading: true, + params, + }); + }, + + /** + * 获取热门搜索词 + */ + getHotSearch() { + return fetchSso({ + url: `${basurl}/search/hot`, + method: 'get', + loading: false, + }); + }, + + /** + * 获取搜索建议 + * @param {String} keyword - 关键词 + */ + getSuggest(keyword) { + return fetchSso({ + url: `${basurl}/search/suggest`, + method: 'get', + loading: false, + params: { keyword }, + }); + }, + + /** + * 获取搜索历史 + */ + getSearchHistory() { + return fetchSso({ + url: `${basurl}/search/history`, + method: 'get', + loading: false, + }); + }, + + /** + * 清除搜索历史 + */ + clearSearchHistory() { + return fetchSso({ + url: `${basurl}/search/history`, + method: 'delete', + loading: false, + }); + }, +}; diff --git a/txw-mhzc-web/src/pages/index/components/search/SearchArea.vue b/txw-mhzc-web/src/pages/index/components/search/SearchArea.vue new file mode 100644 index 0000000..a65f0ad --- /dev/null +++ b/txw-mhzc-web/src/pages/index/components/search/SearchArea.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/txw-mhzc-web/src/pages/index/components/search/SearchCategoryTabs.vue b/txw-mhzc-web/src/pages/index/components/search/SearchCategoryTabs.vue new file mode 100644 index 0000000..31b89bb --- /dev/null +++ b/txw-mhzc-web/src/pages/index/components/search/SearchCategoryTabs.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/txw-mhzc-web/src/pages/index/components/search/SearchEmpty.vue b/txw-mhzc-web/src/pages/index/components/search/SearchEmpty.vue new file mode 100644 index 0000000..6df3a4d --- /dev/null +++ b/txw-mhzc-web/src/pages/index/components/search/SearchEmpty.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/txw-mhzc-web/src/pages/index/components/search/SearchHistoryBar.vue b/txw-mhzc-web/src/pages/index/components/search/SearchHistoryBar.vue new file mode 100644 index 0000000..3cfaaf6 --- /dev/null +++ b/txw-mhzc-web/src/pages/index/components/search/SearchHistoryBar.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/txw-mhzc-web/src/pages/index/components/search/SearchResultItem.vue b/txw-mhzc-web/src/pages/index/components/search/SearchResultItem.vue new file mode 100644 index 0000000..24ca156 --- /dev/null +++ b/txw-mhzc-web/src/pages/index/components/search/SearchResultItem.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/txw-mhzc-web/src/pages/index/components/search/SearchResultList.vue b/txw-mhzc-web/src/pages/index/components/search/SearchResultList.vue new file mode 100644 index 0000000..245dd26 --- /dev/null +++ b/txw-mhzc-web/src/pages/index/components/search/SearchResultList.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/txw-mhzc-web/src/pages/index/components/search/SearchSuggestion.vue b/txw-mhzc-web/src/pages/index/components/search/SearchSuggestion.vue new file mode 100644 index 0000000..b162901 --- /dev/null +++ b/txw-mhzc-web/src/pages/index/components/search/SearchSuggestion.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/txw-mhzc-web/src/pages/index/router/routes.js b/txw-mhzc-web/src/pages/index/router/routes.js index 444f593..b25fa6c 100644 --- a/txw-mhzc-web/src/pages/index/router/routes.js +++ b/txw-mhzc-web/src/pages/index/router/routes.js @@ -87,7 +87,7 @@ function tfwgj() { function tfwxq() { return import('@/pages/index/views/gxfb/tfwxq.vue'); } - + //绿色交易 function lsjy() { return import('@/pages/index/views/lsjy/lsjy.vue'); @@ -98,6 +98,11 @@ function gzt() { return import(/* webpackChunkName: "gzt" */ '@/pages/index/views/gzt/index.vue'); } +// 搜索结果页 +function search() { + return import(/* webpackChunkName: "search" */ '@/pages/index/views/search/index.vue'); +} + @@ -298,4 +303,16 @@ export default [ disableBack: true, }, }, + { + name: 'search', + path: '/search', + component: search, + meta: { + title: '搜索结果', + isShowSideBar: false, + hasHome: true, + breadCrumbs: [{ title: '首页', to: '/home' }, { title: '搜索结果', to: '/search' }], + disableBack: true, + }, + }, ]; diff --git a/txw-mhzc-web/src/pages/index/views/search/index.vue b/txw-mhzc-web/src/pages/index/views/search/index.vue new file mode 100644 index 0000000..d9ed0c1 --- /dev/null +++ b/txw-mhzc-web/src/pages/index/views/search/index.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/txw-mhzc-web/vue.config.js b/txw-mhzc-web/vue.config.js index 63bc457..589296f 100644 --- a/txw-mhzc-web/vue.config.js +++ b/txw-mhzc-web/vue.config.js @@ -286,26 +286,26 @@ module.exports = { // 会误伤 SPA 路由 /view/mhzc/...,刷新时整页请求被转发到后端导致 Proxy error。必须用 ^ 限定为路径前缀。 proxy: { '^/sso': { - // target: 'http://localhost:9301', - target: 'http://carbon.liantu.tech', + target: 'http://localhost:9301', + // target: 'http://carbon.liantu.tech', // target: 'http://10.23.20.13:94/', changeOrigin: true, }, '^/mhzc': { - // target: 'http://localhost:9302', - target: 'http://carbon.liantu.tech', + target: 'http://localhost:9302', + // target: 'http://carbon.liantu.tech', // target: 'http://10.23.20.13:94/', changeOrigin: true, }, '^/gxzx': { - // target: 'http://localhost:9303', - target: 'http://carbon.liantu.tech', + target: 'http://localhost:9303', + // target: 'http://carbon.liantu.tech', // target: 'http://10.23.20.13:94/', changeOrigin: true, }, '^/yygl': { - // target: 'http://localhost:20010', - target: 'http://carbon.liantu.tech', + target: 'http://localhost:20010', + // target: 'http://carbon.liantu.tech', // target: 'http://10.23.20.13:94/', changeOrigin: true, }, diff --git a/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/controller/SearchController.java b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/controller/SearchController.java new file mode 100644 index 0000000..2193a87 --- /dev/null +++ b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/controller/SearchController.java @@ -0,0 +1,75 @@ +package com.css.txw.mhzc.controller; + +import com.css.ggzc.framework.common.pojo.CommonResult; +import com.css.txw.mhzc.pojo.req.SearchReqVO; +import com.css.txw.mhzc.pojo.vo.SearchResultVO; +import com.css.txw.mhzc.service.SearchService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/search") +@Tag(name = "搜索接口") +@Validated +@Slf4j +public class SearchController { + + @Resource + private SearchService searchService; + + @GetMapping + @Operation(summary = "搜索", description = "全站搜索") + public CommonResult> search(@Valid SearchReqVO reqVO) { + List list = searchService.search(reqVO); + + Map result = new HashMap<>(); + result.put("total", list.size()); + result.put("list", list); + + Map categoryCount = new HashMap<>(); + categoryCount.put("all", list.size()); + categoryCount.put("carbon_cert", 0); + categoryCount.put("service", 0); + categoryCount.put("news", list.size()); + result.put("categoryCount", categoryCount); + + return CommonResult.success(result); + } + + @GetMapping("/hot") + @Operation(summary = "热门搜索", description = "获取热门搜索词") + public CommonResult> getHotSearch() { + return CommonResult.success(searchService.getHotSearchKeywords()); + } + + @GetMapping("/suggest") + @Operation(summary = "搜索建议", description = "获取搜索建议") + public CommonResult>> getSuggest(@RequestParam String keyword) { + List suggestions = searchService.getSearchSuggestions(keyword); + Map> result = new HashMap<>(); + result.put("suggestions", suggestions); + return CommonResult.success(result); + } + + @GetMapping("/history") + @Operation(summary = "搜索历史", description = "获取用户搜索历史") + public CommonResult> getSearchHistory() { + return CommonResult.success(searchService.getSearchHistory()); + } + + @DeleteMapping("/history") + @Operation(summary = "清除搜索历史", description = "清除用户搜索历史") + public CommonResult clearSearchHistory() { + searchService.clearSearchHistory(); + return CommonResult.success(true); + } +} diff --git a/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/req/SearchReqVO.java b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/req/SearchReqVO.java new file mode 100644 index 0000000..df99683 --- /dev/null +++ b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/req/SearchReqVO.java @@ -0,0 +1,25 @@ +package com.css.txw.mhzc.pojo.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotBlank; + +@Data +@Schema(description = "搜索请求") +public class SearchReqVO { + + @Schema(description = "关键词", required = true) + @NotBlank(message = "关键词不能为空") + @Length(max = 50, message = "关键词不能超过50个字符") + private String keyword; + + @Schema(description = "分类类型: all, carbon_cert, service, news") + private String categoryType = "all"; + + @Schema(description = "页码") + private Integer page = 1; + + @Schema(description = "每页条数") + private Integer pageSize = 10; +} diff --git a/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/vo/SearchResultVO.java b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/vo/SearchResultVO.java new file mode 100644 index 0000000..ece22c2 --- /dev/null +++ b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/vo/SearchResultVO.java @@ -0,0 +1,39 @@ +package com.css.txw.mhzc.pojo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "搜索结果") +public class SearchResultVO { + + @Schema(description = "内容ID") + private String id; + + @Schema(description = "标题(关键词高亮)") + private String title; + + @Schema(description = "摘要") + private String summary; + + @Schema(description = "缩略图URL") + private String thumbnail; + + @Schema(description = "分类标签(展示用)") + private String category; + + @Schema(description = "分类类型(筛选用)") + private String categoryType; + + @Schema(description = "来源名称") + private String source; + + @Schema(description = "来源类型") + private String sourceType; + + @Schema(description = "发布时间") + private String publishTime; + + @Schema(description = "跳转链接") + private String url; +} diff --git a/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/vo/SyzxxxVO.java b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/vo/SyzxxxVO.java index f2a4b4f..d6bc2b5 100644 --- a/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/vo/SyzxxxVO.java +++ b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/pojo/vo/SyzxxxVO.java @@ -15,4 +15,6 @@ public class SyzxxxVO implements Serializable { private String zxLx; + private String uuid; + } \ No newline at end of file diff --git a/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/service/SearchService.java b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/service/SearchService.java new file mode 100644 index 0000000..e0875ac --- /dev/null +++ b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/service/SearchService.java @@ -0,0 +1,33 @@ +package com.css.txw.mhzc.service; + +import com.css.txw.mhzc.pojo.req.SearchReqVO; +import com.css.txw.mhzc.pojo.vo.SearchResultVO; +import java.util.List; + +public interface SearchService { + + /** + * 搜索 + */ + List search(SearchReqVO reqVO); + + /** + * 获取热门搜索词 + */ + List getHotSearchKeywords(); + + /** + * 获取搜索建议 + */ + List getSearchSuggestions(String keyword); + + /** + * 获取搜索历史 + */ + List getSearchHistory(); + + /** + * 清除搜索历史 + */ + void clearSearchHistory(); +} diff --git a/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/service/impl/SearchServiceImpl.java b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/service/impl/SearchServiceImpl.java new file mode 100644 index 0000000..17c73c8 --- /dev/null +++ b/txw-mhzc/txw-mhzc-service-biz/src/main/java/com/css/txw/mhzc/service/impl/SearchServiceImpl.java @@ -0,0 +1,135 @@ +package com.css.txw.mhzc.service.impl; + +import com.css.txw.mhzc.pojo.req.SearchReqVO; +import com.css.txw.mhzc.pojo.vo.SearchResultVO; +import com.css.txw.mhzc.pojo.vo.SyzxxxVO; +import com.css.txw.mhzc.service.SearchService; +import com.css.txw.mhzc.service.TxwMhzcZxxxbService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class SearchServiceImpl implements SearchService { + + @Resource + private TxwMhzcZxxxbService zxxxbService; + + private static final String HIGHLIGHT_START = ""; + private static final String HIGHLIGHT_END = ""; + + @Override + public List search(SearchReqVO reqVO) { + Map> data = zxxxbService.zxxx(); + List resultList = new ArrayList<>(); + String categoryType = reqVO.getCategoryType(); + + if (data != null) { + data.forEach((category, list) -> { + String currentCategoryType = getCategoryType(category); + if (!"all".equals(categoryType) && !categoryType.equals(currentCategoryType)) { + return; + } + + list.forEach(zxxx -> { + if (matchKeyword(zxxx.getBt1(), reqVO.getKeyword()) || + matchKeyword(zxxx.getZxNr(), reqVO.getKeyword())) { + + SearchResultVO vo = new SearchResultVO(); + vo.setId(zxxx.getUuid()); + vo.setTitle(highlightKeyword(zxxx.getBt1(), reqVO.getKeyword())); + vo.setSummary(highlightKeyword(truncate(zxxx.getZxNr(), 200), reqVO.getKeyword())); + vo.setCategory(category); + vo.setCategoryType(currentCategoryType); + vo.setSource("碳信网"); + vo.setSourceType("news"); + vo.setPublishTime(zxxx.getFbsj() != null ? zxxx.getFbsj().toString() : null); + vo.setUrl("/mhzc/news/" + zxxx.getUuid()); + + resultList.add(vo); + } + }); + }); + } + + int start = (reqVO.getPage() - 1) * reqVO.getPageSize(); + int end = Math.min(start + reqVO.getPageSize(), resultList.size()); + + if (start >= resultList.size()) { + return new ArrayList<>(); + } + + return resultList.subList(start, end); + } + + @Override + public List getHotSearchKeywords() { + return Arrays.asList("江苏电厂配额", "林业碳汇开发", "CBAM 报告", "零碳展会"); + } + + @Override + public List getSearchSuggestions(String keyword) { + if (!StringUtils.hasText(keyword)) { + return new ArrayList<>(); + } + + List suggestions = new ArrayList<>(); + String lowerKeyword = keyword.toLowerCase(); + + suggestions.add(keyword); + + return suggestions.stream() + .filter(s -> s.toLowerCase().contains(lowerKeyword)) + .limit(10) + .collect(Collectors.toList()); + } + + private boolean matchKeyword(String text, String keyword) { + if (!StringUtils.hasText(text) || !StringUtils.hasText(keyword)) { + return false; + } + return text.contains(keyword); + } + + private String highlightKeyword(String text, String keyword) { + if (!StringUtils.hasText(text) || !StringUtils.hasText(keyword)) { + return text; + } + return text.replace(keyword, HIGHLIGHT_START + keyword + HIGHLIGHT_END); + } + + private String truncate(String text, int maxLength) { + if (text == null || text.length() <= maxLength) { + return text; + } + return text.substring(0, maxLength) + "..."; + } + + private String getCategoryType(String category) { + if ("行业专题".equals(category)) { + return "news"; + } else if ("碳证中心".equals(category)) { + return "carbon_cert"; + } else if ("服务中心".equals(category)) { + return "service"; + } + return "all"; + } + + @Override + public List getSearchHistory() { + return new ArrayList<>(); + } + + @Override + public void clearSearchHistory() { + } +}