feat: 合作伙伴更新、反馈页面、新闻初始化语句、接口权限

This commit is contained in:
liulujian 2026-05-04 21:42:07 +08:00
parent bcadef7868
commit c0165b93f8
50 changed files with 3742 additions and 1048642 deletions

28
.claude/settings.json Normal file
View File

@ -0,0 +1,28 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|Bash",
"hooks": [
{
"type": "command",
"command": "code-review-graph update --skip-flows",
"timeout": 30
}
]
}
],
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "code-review-graph status",
"timeout": 10
}
]
}
]
}
}

View File

@ -28,7 +28,16 @@
"Bash(\"/d/Program Files/apache-maven/apache-maven-3.6.3/bin/mvn\" clean package -pl txw-mhzc-service-biz -am -DskipTests)", "Bash(\"/d/Program Files/apache-maven/apache-maven-3.6.3/bin/mvn\" clean package -pl txw-mhzc-service-biz -am -DskipTests)",
"Bash(\"/d/Program Files/jdk8/bin/java\" -Xms256m -Xmx512m -Duser.timezone=Asia/Shanghai -jar target/txw-mhzc-service-biz.jar --spring.profiles.active=local)", "Bash(\"/d/Program Files/jdk8/bin/java\" -Xms256m -Xmx512m -Duser.timezone=Asia/Shanghai -jar target/txw-mhzc-service-biz.jar --spring.profiles.active=local)",
"Bash(mvn compile *)", "Bash(mvn compile *)",
"WebFetch(domain:www.figma.com)" "WebFetch(domain:www.figma.com)",
"mcp__code-review-graph__get_architecture_overview_tool",
"mcp__code-review-graph__list_graph_stats_tool",
"mcp__code-review-graph__list_communities_tool",
"mcp__code-review-graph__build_or_update_graph_tool",
"mcp__code-review-graph__semantic_search_nodes_tool"
]
},
"enableAllProjectMcpServers": true,
"enabledMcpjsonServers": [
"code-review-graph"
] ]
}
} }

View File

@ -0,0 +1,27 @@
---
name: Debug Issue
description: Systematically debug issues using graph-powered code navigation
---
## Debug Issue
Use the knowledge graph to systematically trace and debug issues.
### Steps
1. Use `semantic_search_nodes` to find code related to the issue.
2. Use `query_graph` with `callers_of` and `callees_of` to trace call chains.
3. Use `get_flow` to see full execution paths through suspected areas.
4. Run `detect_changes` to check if recent changes caused the issue.
5. Use `get_impact_radius` on suspected files to see what else is affected.
### Tips
- Check both callers and callees to understand the full context.
- Look at affected flows to find the entry point that triggers the bug.
- Recent changes are the most common source of new issues.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="<your task>")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to "standard" when minimal is insufficient.
- Target: complete any review/debug/refactor task in ≤5 tool calls and ≤800 total output tokens.

View File

@ -0,0 +1,28 @@
---
name: Explore Codebase
description: Navigate and understand codebase structure using the knowledge graph
---
## Explore Codebase
Use the code-review-graph MCP tools to explore and understand the codebase.
### Steps
1. Run `list_graph_stats` to see overall codebase metrics.
2. Run `get_architecture_overview` for high-level community structure.
3. Use `list_communities` to find major modules, then `get_community` for details.
4. Use `semantic_search_nodes` to find specific functions or classes.
5. Use `query_graph` with patterns like `callers_of`, `callees_of`, `imports_of` to trace relationships.
6. Use `list_flows` and `get_flow` to understand execution paths.
### Tips
- Start broad (stats, architecture) then narrow down to specific areas.
- Use `children_of` on a file to see all its functions and classes.
- Use `find_large_functions` to identify complex code.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="<your task>")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to "standard" when minimal is insufficient.
- Target: complete any review/debug/refactor task in ≤5 tool calls and ≤800 total output tokens.

View File

@ -0,0 +1,28 @@
---
name: Refactor Safely
description: Plan and execute safe refactoring using dependency analysis
---
## Refactor Safely
Use the knowledge graph to plan and execute refactoring with confidence.
### Steps
1. Use `refactor_tool` with mode="suggest" for community-driven refactoring suggestions.
2. Use `refactor_tool` with mode="dead_code" to find unreferenced code.
3. For renames, use `refactor_tool` with mode="rename" to preview all affected locations.
4. Use `apply_refactor_tool` with the refactor_id to apply renames.
5. After changes, run `detect_changes` to verify the refactoring impact.
### Safety Checks
- Always preview before applying (rename mode gives you an edit list).
- Check `get_impact_radius` before major refactors.
- Use `get_affected_flows` to ensure no critical paths are broken.
- Run `find_large_functions` to identify decomposition targets.
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="<your task>")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to "standard" when minimal is insufficient.
- Target: complete any review/debug/refactor task in ≤5 tool calls and ≤800 total output tokens.

View File

@ -0,0 +1,29 @@
---
name: Review Changes
description: Perform a structured code review using change detection and impact
---
## Review Changes
Perform a thorough, risk-aware code review using the knowledge graph.
### Steps
1. Run `detect_changes` to get risk-scored change analysis.
2. Run `get_affected_flows` to find impacted execution paths.
3. For each high-risk function, run `query_graph` with pattern="tests_for" to check test coverage.
4. Run `get_impact_radius` to understand the blast radius.
5. For any untested changes, suggest specific test cases.
### Output Format
Provide findings grouped by risk level (high/medium/low) with:
- What changed and why it matters
- Test coverage status
- Suggested improvements
- Overall merge recommendation
## Token Efficiency Rules
- ALWAYS start with `get_minimal_context(task="<your task>")` before any other graph tool.
- Use `detail_level="minimal"` on all calls. Only escalate to "standard" when minimal is insufficient.
- Target: complete any review/debug/refactor task in ≤5 tool calls and ≤800 total output tokens.

2
.gitignore vendored
View File

@ -59,3 +59,5 @@ ewm.png
# 本地开发配置(包含敏感信息) # 本地开发配置(包含敏感信息)
**/bootstrap-local.yml **/bootstrap-local.yml
# Added by code-review-graph
.code-review-graph/

12
.mcp.json Normal file
View File

@ -0,0 +1,12 @@
{
"mcpServers": {
"code-review-graph": {
"command": "uvx",
"args": [
"code-review-graph",
"serve"
],
"type": "stdio"
}
}
}

38
CLAUDE.md Normal file
View File

@ -0,0 +1,38 @@
<!-- code-review-graph MCP tools -->
## MCP Tools: code-review-graph
**IMPORTANT: This project has a knowledge graph. ALWAYS use the
code-review-graph MCP tools BEFORE using Grep/Glob/Read to explore
the codebase.** The graph is faster, cheaper (fewer tokens), and gives
you structural context (callers, dependents, test coverage) that file
scanning cannot.
### When to use graph tools FIRST
- **Exploring code**: `semantic_search_nodes` or `query_graph` instead of Grep
- **Understanding impact**: `get_impact_radius` instead of manually tracing imports
- **Code review**: `detect_changes` + `get_review_context` instead of reading entire files
- **Finding relationships**: `query_graph` with callers_of/callees_of/imports_of/tests_for
- **Architecture questions**: `get_architecture_overview` + `list_communities`
Fall back to Grep/Glob/Read **only** when the graph doesn't cover what you need.
### Key Tools
| Tool | Use when |
|------|----------|
| `detect_changes` | Reviewing code changes — gives risk-scored analysis |
| `get_review_context` | Need source snippets for review — token-efficient |
| `get_impact_radius` | Understanding blast radius of a change |
| `get_affected_flows` | Finding which execution paths are impacted |
| `query_graph` | Tracing callers, callees, imports, tests, dependencies |
| `semantic_search_nodes` | Finding functions/classes by name or keyword |
| `get_architecture_overview` | Understanding high-level codebase structure |
| `refactor_tool` | Planning renames, finding dead code |
### Workflow
1. The graph auto-updates on file changes (via hooks).
2. Use `detect_changes` for code review.
3. Use `get_affected_flows` to understand impact.
4. Use `query_graph` pattern="tests_for" to check coverage.

View File

@ -109,46 +109,35 @@ pack_image() {
MD5_FILE="${PACKAGE_FILE}.md5" MD5_FILE="${PACKAGE_FILE}.md5"
echo "" echo ""
echo ">>> [1/4] 打包镜像 -> ${PACKAGE_FILE}" echo ">>> [1/3] 打包镜像 -> ${PACKAGE_FILE}"
docker save -o "${PACKAGE_FILE}" "${IMAGE_NAME}" docker save -o "${PACKAGE_FILE}" "${IMAGE_NAME}"
echo "" echo ""
echo ">>> [2/4] gzip 压缩 (最高压缩级别)" echo ">>> [2/3] 生成校验文件"
GZIP_FILE="${PACKAGE_FILE}.gz"
gzip -9 -f "${PACKAGE_FILE}"
echo ""
echo ">>> [3/4] 生成校验文件"
md5sum "${GZIP_FILE}" > "${GZIP_FILE}.md5"
md5sum "${PACKAGE_FILE}" > "${MD5_FILE}" md5sum "${PACKAGE_FILE}" > "${MD5_FILE}"
echo "" echo ""
echo ">>> [4/4] 打包完成" echo ">>> [3/3] 打包完成"
ls -lh "${GZIP_FILE}" "${GZIP_FILE}.md5" "${MD5_FILE}" ls -lh "${PACKAGE_FILE}" "${MD5_FILE}"
echo "" echo ""
echo "============================================" echo "============================================"
echo " 下一步操作提示" echo " 下一步操作提示"
echo "============================================" echo "============================================"
echo "" echo ""
echo ">>> 1. 传输镜像到服务器 (推荐使用 .tar.gz 压缩包,体积更小)" echo ">>> 1. 传输镜像到服务器"
echo "" echo ""
echo " # 方式一: scp 传输压缩包" echo " # 方式一: scp 传输"
echo " scp ${GZIP_FILE} ${SERVER_USER}@${SERVER_HOST}:/opt/txw/images/"
echo " scp ${GZIP_FILE}.md5 ${SERVER_USER}@${SERVER_HOST}:/opt/txw/images/"
echo ""
echo " # 方式二: scp 传输原始 tar 包 (不压缩)"
echo " scp ${PACKAGE_FILE} ${SERVER_USER}@${SERVER_HOST}:/opt/txw/images/" echo " scp ${PACKAGE_FILE} ${SERVER_USER}@${SERVER_HOST}:/opt/txw/images/"
echo " scp ${MD5_FILE} ${SERVER_USER}@${SERVER_HOST}:/opt/txw/images/" echo " scp ${MD5_FILE} ${SERVER_USER}@${SERVER_HOST}:/opt/txw/images/"
echo "" echo ""
echo " # 方式二: 手动复制后执行 (推荐生产环境先上传再解压)"
echo ""
echo ">>> 2. SSH 到服务器后,执行以下命令:" echo ">>> 2. SSH 到服务器后,执行以下命令:"
echo "" echo ""
echo " # 创建目录" echo " # 创建目录"
echo " ssh ${SERVER_USER}@${SERVER_HOST} 'mkdir -p /opt/txw/images'" echo " ssh ${SERVER_USER}@${SERVER_HOST} 'mkdir -p /opt/txw/images'"
echo "" echo ""
echo " # 解压 (如果上传的是 .tar.gz)"
echo " ssh ${SERVER_USER}@${SERVER_HOST} 'gunzip /opt/txw/images/${IMAGE_TYPE}-${VERSION}-${TIMESTAMP}.tar.gz'"
echo ""
echo " # 加载镜像" echo " # 加载镜像"
echo " ssh ${SERVER_USER}@${SERVER_HOST} 'docker load -i /opt/txw/images/${IMAGE_TYPE}-${VERSION}-${TIMESTAMP}.tar'" echo " ssh ${SERVER_USER}@${SERVER_HOST} 'docker load -i /opt/txw/images/${IMAGE_TYPE}-${VERSION}-${TIMESTAMP}.tar'"
echo "" echo ""
@ -167,17 +156,14 @@ pack_image() {
echo " -p 8080:8080 \\" echo " -p 8080:8080 \\"
echo " ${IMAGE_NAME}'" echo " ${IMAGE_NAME}'"
echo "" echo ""
echo ">>> 4. 一键执行脚本 (上传 .tar.gz ssh 内执行)" echo ">>> 4. 一键执行脚本 (上传后ssh内执行)"
echo "" echo ""
echo " ssh ${SERVER_USER}@${SERVER_HOST} << 'EOF'" echo " ssh ${SERVER_USER}@${SERVER_HOST} << 'EOF'"
echo " #!/bin/bash" echo " #!/bin/bash"
echo " GZIP_PATH=\"/opt/txw/images/${IMAGE_TYPE}-${VERSION}-${TIMESTAMP}.tar.gz\"" echo " IMAGE_PATH=\"/opt/txw/images/${IMAGE_TYPE}-${VERSION}-${TIMESTAMP}.tar\""
echo " "
echo " # 解压"
echo " gunzip \${GZIP_PATH}"
echo " " echo " "
echo " # 加载镜像" echo " # 加载镜像"
echo " docker load -i \${GZIP_PATH%.gz}" echo " docker load -i \${IMAGE_PATH}"
echo " " echo " "
echo " # 停止并移除旧容器" echo " # 停止并移除旧容器"
echo " docker stop ${SERVICE_NAME} 2>/dev/null || true" echo " docker stop ${SERVICE_NAME} 2>/dev/null || true"
@ -193,9 +179,8 @@ pack_image() {
echo "" echo ""
echo "============================================" echo "============================================"
echo "" echo ""
echo "[*] 打包文件: ${GZIP_FILE} (压缩后,推荐传输)" echo "[*] 打包文件: ${PACKAGE_FILE}"
echo "[*] 原始文件: ${PACKAGE_FILE} (未压缩)" echo "[*] 校验文件: ${MD5_FILE}"
echo "[*] 校验文件: ${GZIP_FILE}.md5 ${MD5_FILE}"
echo "" echo ""
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,504 @@
# 碳信网企业认证模块详解
> 本文档基于代码分析,详细说明企业认证/入驻功能的表结构、流程、API接口及模块关系。
---
## 一、模块定位
企业认证模块分为**两套流程**
1. **企业认证**qyRz/qysp- mhzc-web 使用通过DID扫码认证
2. **企业入驻**qyrz- gxzx-web/yygl-web 使用,通过表单申请+审核
这两个流程最终都会在 `txw_mhzc_qyxxb` 表中创建企业信息记录。
---
## 二、核心表结构
### 2.1 企业申请记录表
#### txw_mhzc_qyrzsqjlb企业认证申请记录表
```sql
CREATE TABLE txw_mhzc_qyrzsqjlb (
uuid VARCHAR(64) PRIMARY KEY, -- 申请记录UUID
qymc VARCHAR(256), -- 企业名称
nsrsbh VARCHAR(64), -- 统一社会信用代码
sqsm TEXT, -- 申请说明
lrruuid VARCHAR(64), -- 录入人UUID
lrrq DATETIME, -- 录入日期
shrsfid VARCHAR(64), -- 审核人UUID
shjg1 VARCHAR(10), -- 审核结果 (2待审核/3通过/4不通过/5已撤销)
shyj1 TEXT, -- 审核意见
shsj DATETIME, -- 审核时间
sjhm1 VARCHAR(32), -- 手机号码
dlzh VARCHAR(64), -- 登录账号
zsxm1 VARCHAR(64), -- 真实姓名
yh_uuid VARCHAR(64) -- 用户UUID
);
```
#### txw_gxzx_rzsqjlb入驻申请记录表
```sql
-- 表结构与 txw_mhzc_qyrzsqjlb 基本相同
CREATE TABLE txw_gxzx_rzsqjlb (
uuid VARCHAR(64) PRIMARY KEY,
qymc VARCHAR(256),
nsrsbh VARCHAR(64),
sqsm TEXT,
ywlx_dm VARCHAR(10), -- 业务类型代码 (01)
sqly TEXT, -- 申请来源
lrruuid VARCHAR(64),
lrrq DATETIME,
shrsfid VARCHAR(64),
shjg1 VARCHAR(10),
shyj1 TEXT,
shsj DATETIME,
sjhm1 VARCHAR(32),
dlzh VARCHAR(64),
zsxm1 VARCHAR(64),
yh_uuid VARCHAR(64)
);
```
### 2.2 企业信息表
#### txw_mhzc_qyxxb企业信息表
```sql
CREATE TABLE txw_mhzc_qyxxb (
qyuuid VARCHAR(64) PRIMARY KEY, -- 企业UUID
qymc VARCHAR(256), -- 企业名称
nsrsbh VARCHAR(64), -- 统一社会信用代码
gxdtrzbz VARCHAR(10), -- 供需大厅入驻标志 (Y/N)
yxbz VARCHAR(10), -- 有效标志 (Y/N)
ywqd_dm VARCHAR(32), -- 业务渠道代码
lrrq DATETIME, -- 录入日期
xgrq DATETIME, -- 修改日期
sjcsdq VARCHAR(32), -- 数据产生地区
sjgsdq VARCHAR(32), -- 数据归属地区
xgrsfid VARCHAR(64), -- 修改人身份ID
lrrsfid VARCHAR(64) -- 录入人身份ID
);
```
### 2.3 用户企业关联表
#### txw_mhzc_yhqygxb用户企业关联表
```sql
CREATE TABLE txw_mhzc_yhqygxb (
uuid VARCHAR(64) PRIMARY KEY,
qyuuid VARCHAR(64), -- 企业UUID
yh_uuid VARCHAR(64), -- 用户UUID
-- 其他字段...
);
```
---
## 三、认证流程详解
### 3.1 企业认证DID扫码方式- mhzc-web
**用户视角流程:**
```
未认证状态
┌─────────────────────────────────────────────────┐
│ 显示"您未认证任何企业信息"提示 │
│ 点击"去认证" → 进入认证流程 │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 显示 DID 二维码 │
│ 用户使用 DID APP 扫描二维码 │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 轮询查询认证结果 │
│ GET /did/pub/backresult/login?reqId=xxx │
│ status=6 → 认证成功,显示企业信息卡片 │
│ status=5 → 认证失败,显示错误信息 │
└─────────────────────────────────────────────────┘
```
**后端处理流程:**
```
DID服务器回调 POST /did/pub/callback/busi
┌─────────────────────────────────────────────────┐
│ 验证 VerifiablePresentation │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 检查企业是否已认证 │
│ yhxxService.getYhxxByDid(holderDid) │
│ 已认证 → 返回"不能重复认证"错误 │
└─────────────────────┬───────────────────────────┘
│ 未认证
┌─────────────────────────────────────────────────┐
│ 验证企业名称一致性 │
│ DID中的entname 与 用户真实姓名匹配 │
│ 不匹配 → 返回"名称不一致"错误 │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 创建/更新企业信息 │
│ TxwMhzcQyxxbServiceImpl.intQyxxByDid() │
│ 1. 检查企业是否已存在按nsrsbh
│ 2. 不存在 → 新建企业记录 │
│ 3. 绑定用户与企业关系yhqygxb
│ 4. 切换Session绑定企业到用户 │
└─────────────────────────────────────────────────┘
```
### 3.2 企业入驻(表单申请方式)- gxzx-web
**用户视角流程:**
```
未认证状态
┌─────────────────────────────────────────────────┐
│ 点击"发布"按钮 │
│ 检查企业认证状态 │
│ 未认证 → 提示"请先完成企业认证" │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 填写入驻表单 │
│ - 企业名称 │
│ - 统一社会信用代码 │
│ - 申请说明 │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 提交申请 │
│ POST /gxzx/qyrz/qyrzsq │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 等待审核 │
│ 显示"待审核"状态 │
│ 可撤回申请 │
└─────────────────────────────────────────────────┘
```
**后端处理流程:**
```
POST /gxzx/qyrz/qyrzsq
┌─────────────────────────────────────────────────┐
│ 创建入驻申请记录 │
│ TxwGxzxRzsqjlbServiceImpl.qyrzsq() │
│ - 插入 txw_gxzx_rzsqjlb 表 │
│ - ywlx_dm = "01" │
│ - shjg1 = "2" (待审核) │
│ - 关联当前用户信息 │
└─────────────────────────────────────────────────┘
```
### 3.3 企业认证审核 - yygl-web运营后台
**审核流程:**
```
运营人员登录 yygl-web
┌─────────────────────────────────────────────────┐
│ 进入"企业认证审核"菜单 │
│ 显示待审核列表 │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 查看企业详情 │
│ 企业名称、信用代码、手机号、申请时间、申请理由 │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 选择审核结果 │
│ - 通过shjg1 = 3
│ - 不通过shjg1 = 4
│ + 填写审核意见 │
└─────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 提交审核 │
│ POST /mhzc/company/qyRzSp │
└─────────────────────────────────────────────────┘
```
**后端处理:**
```
POST /mhzc/company/qyRzSp
┌─────────────────────────────────────────────────┐
│ TxwMhzcQyxxbServiceImpl.qyRzSp() │
│ 1. 更新申请记录状态shjg1, shyj1, shsj
│ 2. if 通过: │
│ - 创建企业信息txw_mhzc_qyxxb
│ - 创建用户企业关联txw_mhzc_yhqygxb
│ - 切换Session绑定企业 │
│ - 发送站内消息(审核通过模板) │
│ 3. if 不通过: │
│ - 发送站内消息(审核不通过模板) │
└─────────────────────────────────────────────────┘
```
---
## 四、模块分布
### 4.1 前端模块
| 项目 | 页面 | 功能 |
|------|------|------|
| txw-mhzc-web | `views/qyrz/index.vue` | 企业认证DID方式 |
| txw-mhzc-web | `views/qy-rz/index.vue` | 企业入驻(预留) |
| txw-gxzx-web | `views/gxzx/index.vue` | 供需大厅发布时触发认证检查 |
| txw-yygl-web | `views/qysp/index.vue` | 企业认证审核(运营) |
| txw-yygl-web | `views/qyrz/index.vue` | 企业入驻审核(运营) |
| txw-mhzc-web | `views/gzt/components/EnterpriseCert.vue` | 工作台企业认证卡片 |
### 4.2 后端模块
| 服务 | Controller | Service | 说明 |
|------|------------|---------|------|
| txw-mhzc | `QyRzController` | `TxwGxzxRzsqjlbService` | 入驻申请 |
| txw-mhzc | `CompanyController` | `TxwMhzcQyxxbService` | 企业认证审核 |
| txw-gxzx | `QyRzController` | `TxwGxzxRzsqjlbService` | 入驻申请gxzx独立 |
| txw-sso | `DidController` | `YhxxService` | DID认证 |
---
## 五、API接口
### 5.1 用户端API
| 接口 | 服务 | 方法 | 参数 | 说明 |
|------|------|------|------|------|
| `/mhzc/qyrz/qyrzsq` | mhzc | POST | `QyRzSqReqVO` | 提交入驻申请 |
| `/gxzx/qyrz/qyrzsq` | gxzx | POST | `QyRzSqReqVO` | 提交入驻申请 |
| `/mhzc/qyrz/currentQyrzList` | mhzc | POST | - | 查询当前用户入驻列表 |
| `/gxzx/qyrz/qyrzList` | gxzx | POST | `QyrzListReqVO` | 查询入驻申请列表 |
| `/mhzc/company/getCurrentRegisterList` | mhzc | POST | - | 查询当前认证记录 |
| `/mhzc/company/revokeSq` | mhzc | GET | uuid | 撤回申请 |
### 5.2 运营管理API
| 接口 | 服务 | 方法 | 参数 | 说明 |
|------|------|------|------|------|
| `/mhzc/company/qyRzSp` | mhzc | POST | `CompanySpReqVO` | 认证审核 |
| `/mhzc/company/getRegisterList` | mhzc | POST | `CompanySpReqVO` | 认证列表 |
| `/gxzx/qyrz/qyrzsp` | gxzx | POST | `QyRzSpReqVO` | 入驻审核 |
| `/gxzx/qyrz/qyrzList` | gxzx | POST | `QyrzListReqVO` | 入驻列表 |
| `/gxzx/company/getQyxxListByYwlx` | gxzx | GET | ywlx | 查询已入驻企业 |
### 5.3 DID认证API
| 接口 | 方法 | 说明 |
|------|------|------|
| `/did/busilice/qrcode` | POST | 获取企业实名认证二维码 |
| `/did/pub/getvp/busi` | GET | 获取企业实名凭证 |
| `/did/pub/callback/busi` | POST | 企业实名回调 |
| `/did/pub/backresult/busi` | GET | 查询认证结果 |
---
## 六、审核状态流转
### 6.1 状态码定义
| 状态码 | 含义 | 说明 |
|--------|------|------|
| `2` | 待审核 | 初始状态 |
| `3` | 通过 | 审核通过 |
| `4` | 不通过 | 审核拒绝 |
| `5` | 已撤销 | 用户撤回 |
### 6.2 状态流转图
```
┌─────────┐
│ (新建) │
└────┬────┘
│ 提交申请
┌─────────┐
│ 待审核 │ shjg1=2
│ (2) │
└────┬────┘
┌────┴────┐
│ │
通过│ │不通过
│ │
▼ ▼
┌────────┐ ┌────────┐
│ 通过 │ │ 不通过 │
│ (3) │ │ (4) │
└────────┘ └────────┘
│ 用户撤回
┌────────┐
│ 已撤销 │
│ (5) │
└────────┘
```
---
## 七、模块间关系
### 7.1 企业认证/入驻与用户的关系
```
用户登录txw-sso
用户发起企业认证/入驻申请
├── mhzc-web: DID扫码认证
│ └─→ 创建 txw_mhzc_qyxxb通过
└── gxzx-web: 填表申请入驻
└─→ 创建 txw_gxzx_rzsqjlb待审核
运营后台审核yygl-web
├── 通过 → 创建 txw_mhzc_qyxxb + txw_mhzc_yhqygxb
└── 不通过 → 更新状态为4
企业认证完成后:
用户可以使用以下功能:
- 发布供需信息需gxdtrzbz=Y
- 申请金融产品
- 发布碳资产交易
```
### 7.2 关键字段说明
| 字段 | 表 | 说明 |
|------|-----|------|
| `gxdtrzbz` | txw_mhzc_qyxxb | 供需大厅入驻标志,决定用户能否发布供需 |
| `yxbz` | txw_mhzc_qyxxb | 企业信息有效性 |
| `ywlxDm` | txw_gxzx_rzsqjlb | 业务类型01=入驻 |
| `shjg1` | txw_mhzc_qyrzsqjlb | 审核结果 |
---
## 八、前端交互详解
### 8.1 mhzc-web 企业认证页面qyrz/index.vue
**页面状态机:**
| 状态 | 条件 | 显示内容 |
|------|------|---------|
| Tab1 | 有待审核/已通过记录 | 申请记录列表 + "去认证"按钮 |
| Tab2 | 未认证 | DID扫码界面 + 用户信息 |
| Tab3 | 已认证yhxx.qymc存在 | 企业认证信息卡片 |
**核心代码逻辑:**
```javascript
// 初始化时检查企业认证状态
async yhinit() {
const { data } = await api.init();
this.yhxx = data;
if (this.yhxx?.qymc) {
this.pagetab = 3; // 已认证,显示认证卡片
} else {
this.Getrzqrcode(); // 未认证,显示二维码
}
}
// 轮询查询认证结果
async pollLoginResult() {
const { data } = await rzbackresultlogin(this.reqId);
if (data.status == 6) {
// 认证成功
this.stopPolling();
this.yhinit();
} else if (data.status == 5) {
// 认证失败
this.stopPolling();
MessagePlugin.info({ content: data.msg });
}
}
```
### 8.2 yygl-web 审核页面
**qysp/index.vue** - 企业认证审核
- 查询条件:企业名称、信用代码、审核状态、申请时间
- 审核操作:弹出对话框选择"通过/不通过" + 填写意见
- API调用`qyRzSp()` → `/mhzc/company/qyRzSp`
**qyrz/index.vue** - 企业入驻审核
- 查询条件同上
- 审核操作同上
- API调用`qyRzSp()` → `/gxzx/qyrz/qyrzsp`
---
## 九、错误处理
### 9.1 用户端错误
| 场景 | 提示信息 |
|------|---------|
| 企业已认证 | "企业已认证" |
| 申请理由为空 | 校验提示"申请说明必填" |
| 统一社会信用代码格式错误 | 前端校验提示 |
| 审核不通过后重申请 | 允许重新提交 |
### 9.2 审核端错误
| 场景 | 逻辑 |
|------|------|
| 重复认证 | DidController 返回"不能重复认证" |
| 名称不一致 | 返回"名称不一致"错误 |
| 企业不存在 | 查询返回null跳过创建 |
---
## 十、外部依赖
| 依赖服务 | 用途 |
|---------|------|
| **xxzxApi消息中心** | 发送审核结果站内消息 |
| **IMhzcApi** | 获取用户信息、切换Session |
| **SwitchSessionApi** | 绑定用户与企业关系 |
| **DID服务** | 企业实名认证org.chainweaver.did.sdk |
### 10.1 消息模板
| 模板ID | 用途 |
|--------|------|
| `gxdt_rzsp_sptg` | 入驻审核通过gxzx |
| `gxdt_rzsp_spsb` | 入驻审核不通过gxzx |
| `mhzc_qyrz_sptg` | 认证审核通过mhzc |
| `mhzc_qyrz_spsb` | 认证审核不通过mhzc |
---
*文档生成时间: 2026-05-03*
*基于企业认证模块代码分析*

738
docs/功能说明书.md Normal file
View File

@ -0,0 +1,738 @@
# 碳信网项目功能说明书
> 本文档基于代码实现分析生成排除干扰项Axure原型文件、node_modules、部署脚本等专注于实际功能。
---
## 一、项目定位
碳信网是一个**碳排放管理、碳资产交易及相关服务的综合平台**,包含:
- **公众门户**txw-gxzx-web面向企业提供碳服务供需、碳金融、碳交易等功能
- **碳门户主页**txw-mhzc-web面向用户的碳服务平台首页
- **运营管理后台**txw-yygl-web运营人员使用的管理后台
---
## 二、功能模块层级图
```
├─ P0 认证模块
│ ├─ 用户名密码登录
│ ├─ 短信验证码登录
│ ├─ OAuth2第三方登录
│ ├─ 登出
│ └─ 会话初始化
├─ P0 碳服务供需模块gxzx
│ ├─ 供需大厅浏览
│ ├─ 供需信息发布(供给/需求)
│ ├─ 供需收藏
│ └─ 企业标签筛选
├─ P0 碳金融模块lsjr/lsxd
│ ├─ 金融机构列表(银行/保险)
│ ├─ 金融产品列表
│ ├─ 产品详情查看
│ └─ 金融产品申请(信贷/保险)
├─ P0 碳交易模块lsjy
│ ├─ 碳资产列表浏览
│ ├─ 碳资产详情
│ ├─ 碳资产发布
│ └─ 碳资产上下架
├─ P0 企业入驻模块
│ ├─ 企业注册申请
│ ├─ 企业认证审核
│ └─ 企业入驻审核
├─ P1 用户中心
│ ├─ 工作台首页
│ ├─ 账号管理
│ ├─ 我的供给
│ ├─ 我的需求
│ ├─ 绿色交易管理
│ └─ 消息中心
├─ P1 运营管理
│ ├─ 用户管理
│ ├─ 轮播图管理
│ ├─ 公告发布管理
│ ├─ 服务供给审核
│ ├─ 需求审核
│ ├─ 绿色金融产品管理
│ ├─ 绿色交易审批
│ └─ 企业审批管理
├─ P1 搜索模块
│ ├─ 全局搜索
│ ├─ 热门搜索词
│ ├─ 搜索建议
│ └─ 搜索历史
├─ P2 企业出海模块
│ ├─ 电池法案解读
│ ├─ CBAM政策指引
│ └─ 航运燃料专题
├─ P2 碳能力平台
│ ├─ 碳核算平台
│ ├─ 碳认证机构
│ ├─ 碳交易平台
│ ├─ 碳金融服务
│ └─ 碳技术咨询
└─ P2 勋章系统
├─ 勋章配置管理
├─ 勋章审核列表
├─ 明星管理
└─ 抽奖记录管理
```
---
## 三、核心功能详解
### 3.1 认证模块Authentication
**功能流程:**
```
用户输入账号密码
┌─────────────────┐
│ 登录表单验证 │ ← 检查图形验证码、短信验证码
└────────┬────────┘
┌─────────────────┐
│ 调用登录API │ POST /sso/auth/login
└────────┬────────┘
成功 │ 失败
─────┼─────
┌─────────────────┐
│ 保存Token │ 存储到sessionStorage
└────────┬────────┘
┌─────────────────┐
│ 初始化用户会话 │ POST /mhzc/user/init
└────────┬────────┘
┌─────────────────┐
│ 跳转首页/工作台 │
└─────────────────┘
```
**API清单**
| 功能 | 接口 | 说明 |
|------|------|------|
| 账号密码登录 | `POST /sso/auth/login` | |
| 短信验证码登录 | `POST /sso/auth/loginBySMS` | |
| 第三方登录 | `POST /sso/starAuth/login` | mxlogin |
| OAuth2授权 | `POST /oauth2/authorize` | |
| 获取验证码 | `POST /sso/verify/get` | |
| 发送短信 | `POST /sso/auth/sendMsg` | |
| 登出 | `POST /sso/auth/logout` | |
| 会话初始化 | `POST /mhzc/user/init` | 获取用户信息和入驻状态 |
---
### 3.2 碳服务供需模块(供需大厅)
**功能流程:**
```
供需大厅首页
├── [供给] ───┬── 服务类型筛选
│ ├── 行业筛选
│ ├── 地区筛选
│ ├── 价格筛选
│ └── 关键词搜索
├── [需求] ───┬── 需求类型筛选(与供给类似)
└── [发布] ──┬── 检查企业认证状态
├── 未认证 → 提示去认证
└── 已认证 → 显示发布表单
├── 选择类型(供给/需求)
├── 填写标题、服务类型、行业、价格等
├── 设置有效期、联系人信息
└── 提交发布
```
**发布字段:**
| 字段 | 说明 | 必填 |
|------|------|------|
| ywlxDm | 业务类型01供给/02需求 | 是 |
| title | 标题 | 是 |
| fwlx | 服务类型 | 是 |
| sshy | 所属行业 | 是 |
| price | 价格/预算 | 是 |
| qyuuid | 企业UUID | 是 |
| xzqh | 行政区划 | 否 |
| jzsj | 截止时间 | 否 |
| bq | 标签 | 否 |
| lxr | 联系人 | 否 |
| lxfs | 联系方式 | 否 |
| description | 详细描述 | 否 |
**API清单**
| 功能 | 接口 | 说明 |
|------|------|------|
| 供需列表 | `POST /gxzx/gxdt/gxxxList` | 支持分页、类型筛选 |
| 发布供需 | `POST /gxzx/gxdt/gxfb` | |
| 收藏/取消收藏 | `POST /gxzx/gxdt/gxsc` | type: add/remove |
| 企业标签查询 | `POST /gxzx/gxdt/getQyuuidsByBq` | |
| 代码表查询 | `POST /yygl/cache/v1/list/${tableName}` | fwlx/xqlx/sshy等 |
**模块关系:**
- 供需大厅与企业入驻模块联动:未认证企业不能发布供需信息
- 供需信息需要审核后才能展示通过运营后台tfwgj/tfwxq审核
---
### 3.3 碳金融模块(绿色信贷/保险)
**功能流程:**
```
碳金融首页
├── [绿色信贷] ───┬── 显示银行机构列表
│ └── 查看产品列表
└── [绿色保险] ───┬── 显示保险机构列表
└── 查看产品列表
查看产品详情
├── 显示产品信息(额度、期限、利率/保费等)
└── [立即申请]
申请表单3步
├── Step1: 填写信息
│ ├── 企业信息
│ ├── 联系人信息
│ └── 碳数据来源选择
├── Step2: 上传材料
│ └── 支持文件上传
└── Step3: 提交成功
```
**产品详情字段:**
| 字段 | 信贷产品 | 保险产品 |
|------|---------|---------|
| cpname | 产品名称 | 产品名称 |
| cpleixing | 产品类型 | 产品类型 |
| fgje | 最高额度 | 最高保额 |
| qx | 期限 | 保障期限 |
| fl | 费率 | 保费 |
| cppj | 产品配件/附加服务 | |
**API清单**
| 功能 | 接口 | 说明 |
|------|------|------|
| 金融机构列表 | `POST /gxzx/lsjr/queryJgList` | type: xd/bx |
| 产品列表 | `POST /gxzx/lsjr/queryXdbxcpList` | 分页查询 |
| 产品详情 | `GET /gxzx/lsjr/queryXdbxcpxq` | |
| 信贷申请 | `POST /gxzx/lsjr/saveTbsqxx` | multipart |
| 保险申请 | `POST /gxzx/lsjr/saveTbsqxx` | multipart |
**模块关系:**
- 金融产品管理在运营后台yygl-web/lscp/LsjrIndex.vue
- 申请记录查看在运营后台yygl-web/lscp/LsjrXq.vue
---
### 3.4 碳交易模块(绿色交易)
**功能流程:**
```
绿色交易大厅
├── 资产类型筛选
│ ├── 绿电绿证ZC01
│ ├── 碳配额ZC02
│ └── CCERZC03
├── 价格区间筛选
└── 有效期筛选
查看资产详情
└── [联系/申请]
```
**资产发布字段:**
| 字段 | 说明 |
|------|------|
| zclx | 资产类型 |
| sl | 数量 |
| dj | 单价 |
| yxrq | 有效期 |
| zcnf | 资产年份 |
| lxr | 联系人 |
| lxfs | 联系方式 |
**API清单**
| 功能 | 接口 | 说明 |
|------|------|------|
| 资产类型列表 | `GET /gxzx/lsjy/queryZclxList` | |
| 资产列表 | `POST /gxzx/lsjy/queryZcxxList` | 分页 |
| 资产详情 | `GET /gxzx/lsjy/queryZcxxxq` | |
| 发布/更新资产 | `POST /gxzx/lsjy/saveOrUpdate` | |
| 上下架 | `POST /gxzx/lsjy/zcsxj` | |
| 审批 | `POST /gxzx/lsjy/zcSp` | |
| 删除 | `POST /gxzx/lsjy/delete/${zcid}` | |
**模块关系:**
- 交易资产需要审核运营后台lsjy审批
- 与运营后台的绿色交易管理联动
---
### 3.5 企业入驻模块
**功能流程:**
```
企业入口选择
├── [企业认证] ← 已有企业资质,申请认证
│ │
│ ├── 填写企业信息
│ │ ├── 企业名称
│ │ ├── 统一社会信用代码
│ │ └── 营业执照等资质
│ └── 提交认证申请
│ │
│ ▼
│ 运营后台审核(通过/拒绝)
└── [企业入驻] ← 新企业申请入驻平台
├── 填写入驻信息
│ ├── 企业基本信息
│ ├── 联系人信息
│ └── 期望服务类型
└── 提交入驻申请
运营后台审核
```
**认证/入驻字段:**
| 字段 | 说明 |
|------|------|
| qymc | 企业名称 |
| tyshxydm | 统一社会信用代码 |
| frdb | 法定代表人 |
| lxr | 联系人 |
| lxfs | 联系方式 |
| qydz | 企业地址 |
| rzxs | 入驻形式 |
| qwlx | 期望服务类型 |
**API清单**
| 功能 | 接口 | 说明 |
|------|------|------|
| 企业注册 | `POST /gxzx/qyrz/qyrzsq` | gxzx-web |
| 企业注册 | `POST /mhzc/company/register` | mhzc-web |
| 认证申请 | `POST /mhzc/company/register` | |
| 认证列表 | `POST /mhzc/company/getRegisterList` | 运营后台 |
| 认证审核 | `POST /mhzc/company/qyRzSp` | |
| 入驻列表 | `POST /gxzx/qyrz/qyrzList` | |
| 入驻审核 | `POST /gxzx/qyrz/qyrzsp` | |
**模块关系:**
- 企业认证/入驻状态决定用户可用功能
- 状态存储在init接口返回的`gxdtRzbz`字段
---
### 3.6 用户中心
**功能流程:**
```
用户中心首页(工作台)
├── 概览统计
│ ├── 供给数量
│ ├── 需求数量
│ └── 交易数量
├── 快捷操作
│ ├── 发布供给
│ ├── 发布需求
│ └── 查看更多
├── 我的供给列表
├── 我的需求列表
└── 政策新闻/公告
```
**用户中心子模块:**
| 模块 | 功能 |
|------|------|
| 工作台 | 统计概览、快捷操作、我的供给/需求 |
| 账号管理 | 个人信息、密码修改 |
| 企业认证 | 认证状态查询、认证申请 |
| 企业入驻 | 入驻状态查询、入驻申请 |
| 我的供给 | 查看我发布的供给信息 |
| 我的需求 | 查看我发布的需求信息 |
| 绿色交易 | 我发布的碳资产 |
| 消息中心 | 系统公告、我的消息 |
**API清单**
| 功能 | 接口 | 说明 |
|------|------|------|
| 用户初始化 | `POST /mhzc/user/init` | 获取用户+企业信息 |
| 工作台统计 | `GET /gxzx/gxdt/getStats` | 获取统计数据 |
| 公告列表 | `POST /yygl/xx/getXxByXxflDmPage` | |
| 发布公告 | `POST /yygl/xx/xxfb` | |
---
### 3.7 运营管理模块
**功能流程:**
```
运营管理系统首页
├── 企业管理
│ ├── 企业认证审核 ← 审核企业资质
│ └── 企业入驻审核 ← 审核入驻申请
├── 用户管理
│ ├── 用户列表 ← 查看所有用户
│ ├── 冻结/解冻用户
│ └── 重置密码
├── 内容管理
│ ├── 轮播图管理 ← 首页Banner配置
│ └── 公告发布管理 ← 系统公告
├── 碳服务管理
│ ├── 服务供给审核 ← 审核供给信息
│ └── 需求审核 ← 审核需求信息
├── 绿色金融管理
│ ├── 产品管理 ← 增删改金融产品
│ └── 申请查看 ← 查看信贷/保险申请
└── 绿色交易管理
├── 交易管理 ← 查看/上下架资产
└── 交易审批 ← 审核新发布资产
```
**审核流程:**
```
用户提交申请
运营后台显示待审核列表
运营人员查看详情
├── [通过] → 更新状态为已通过
│ 信息公开展示
└── [拒绝] → 填写拒绝原因
更新状态为已拒绝
用户可修改后重新提交
```
**API清单**
| 功能 | 接口 | 说明 |
|------|------|------|
| 认证审核 | `POST /mhzc/company/qyRzSp` | |
| 入驻审核 | `POST /gxzx/qyrz/qyrzsp` | |
| 服务审核 | `POST /gxzx/gxdt/gxsp` | ywlxDm=01 |
| 需求审核 | `POST /gxzx/gxdt/gxsp` | ywlxDm=02 |
| 交易审批 | `POST /gxzx/lsjy/zcSp` | |
| 用户列表 | `POST /mhzc/user/getAllUser` | |
| 冻结用户 | `POST /mhzc/user/lockUser` | |
| 解冻用户 | `POST /mhzc/user/unlockUser` | |
| 重置密码 | `POST /sso/auth/resetPassword` | |
---
### 3.8 搜索模块
**功能流程:**
```
搜索入口
├── 输入关键词
│ │
│ ├── 显示搜索建议(下拉提示)
│ │
│ └── 回车/点击搜索
│ │
│ ▼
│ 搜索结果页
│ │
│ ├── 按类型分类展示
│ │ ├── 资讯
│ │ ├── 企业
│ │ ├── 产品
│ │ └──供需信息
│ │
│ └── 热门搜索词展示
└── 热门搜索词点击直接搜索
```
**API清单**
| 功能 | 接口 | 说明 |
|------|------|------|
| 全局搜索 | `GET /mhzc/search` | keyword, categoryType |
| 热门搜索 | `GET /mhzc/search/hot` | |
| 搜索建议 | `GET /mhzc/search/suggest` | keyword |
| 搜索历史 | `GET /mhzc/search/history` | |
| 清除历史 | `DELETE /mhzc/search/history` | |
---
### 3.9 企业出海模块
**功能流程:**
```
企业出海专题页
├── [电池法案] ← EU Battery Act
│ ├── 政策要求
│ ├── 合规指引
│ └── 案例分享
├── [CBAM] ← Carbon Border Adjustment Mechanism
│ ├── 政策解读
│ ├── 合规指引
│ └── 案例分析
└── [航运燃料] ← Shipping Fuel
├── 政策要求
├── 合规指引
└── 案例分享
```
**模块关系:**
- 企业出海模块与碳服务供需模块独立
- 不涉及交易流程,仅信息展示
- 提供合规服务申请入口
---
### 3.10 碳能力平台
**功能流程:**
```
碳能力平台首页
├── [碳核算平台] ← 碳核算机构目录
├── [碳认证机构] ← 碳认证服务目录
├── [碳交易平台] ← 碳交易机构目录
├── [碳金融服务] ← 碳金融服务机构
└── [碳技术咨询] ← 碳技术咨询服务
```
**模块关系:**
- 仅做机构展示
- 点击可查看机构详情
- 不涉及交易流程
---
### 3.11 勋章系统mhcz-web特有
**功能流程:**
```
勋章管理(运营后台)
├── 勋章配置
│ ├── 配置勋章名称、图标、规则
│ └── 配置勋章状态(启用/停用)
├── 勋章审核
│ ├── 查看用户申请列表
│ └── 审核通过/拒绝
└── 勋章模板
├── 管理勋章模板
└── 模板明细查询
明星管理
├── 明星列表
│ ├── 明星基本信息
│ └── 明星认证状态
└── 期望明星统计
└── 统计分析用户期望的明星
```
**API清单**
| 功能 | 接口 | 说明 |
|------|------|------|
| 勋章配置列表 | `POST /yhzxApi/xz/getXzpzList` | |
| 勋章审核列表 | `POST /yhzxApi/xz/getXzsqList` | |
| 明星列表 | `POST /yhzxApi/user/getStarList` | |
| 创建明星 | `POST /yhzxApi/user/createStar` | |
| 抽奖记录 | `POST /yhzxApi/shdz/getCjjlPage` | |
---
## 四、模块间关系图
```
┌──────────────┐
│ SSO认证 │
│ 系统 │
└──────┬───────┘
│ 所有模块依赖
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ gxzx-web │ │ mhzc-web │ │ yygl-web │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 供需大厅 │ │ 服务中心 │ │ 运营管理 │
│ 碳金融 │ │ 用户中心 │ │ 企业审核 │
│ 碳交易 │ │ 碳交易市场 │ │ 内容管理 │
└──────────┘ └──────────┘ └──────────┘
┌──────────────┐
│ 企业入驻模块 │
│ (共享) │
└──────────────┘
```
**依赖关系说明:**
| 模块 | 依赖模块 | 说明 |
|------|---------|------|
| 供需大厅 | 企业入驻 | 未认证不能发布 |
| 碳金融 | 企业入驻 | 申请需要企业信息 |
| 碳交易 | 企业入驻 | 发布需要企业信息 |
| 用户中心 | 认证模块 | 需要登录态 |
| 运营管理 | 认证模块 | 需要管理员权限 |
| 运营管理 | 企业入驻 | 审核入驻申请 |
---
## 五、数据流概览
### 5.1 用户会话数据
```
登录 → sessionStorage['yhxx']
├── userId
├── username
├── companyUuid
├── gxdtRzbz (企业认证状态)
└── ... 其他用户信息
```
### 5.2 核心数据状态
| 状态字段 | 说明 | 影响功能 |
|---------|------|---------|
| gxdtRzbz | 企业认证状态 | 供需发布、金融申请 |
| qyrzSpzt | 入驻审核状态 | 企业入驻功能 |
| sjfwZt | 上下架状态 | 信息是否展示 |
### 5.3 审核流程数据
```
提交申请
pending (待审核)
approved/rejected (通过/拒绝)
if approved → 公开显示
if rejected → 可修改重提交
```
---
## 六、功能优先级汇总
### P0 - 核心功能(系统运行必需)
| 模块 | 功能 | 说明 |
|------|------|------|
| 认证 | 账号密码登录 | 基础认证 |
| 认证 | 会话初始化 | 获取用户权限信息 |
| 供需 | 供需大厅浏览 | 核心业务入口 |
| 供需 | 供需发布 | 核心业务操作 |
| 金融 | 产品列表/详情 | 金融业务入口 |
| 金融 | 金融申请 | 核心业务操作 |
| 交易 | 碳资产交易 | 核心业务 |
| 企业入驻 | 企业注册/认证 | 基础业务 |
### P1 - 重要功能(业务完整性)
| 模块 | 功能 | 说明 |
|------|------|------|
| 运营管理 | 企业审核 | 运营基础功能 |
| 运营管理 | 内容管理 | 运营基础功能 |
| 运营管理 | 用户管理 | 运营基础功能 |
| 用户中心 | 工作台 | 用户入口 |
| 搜索 | 全局搜索 | 信息检索 |
### P2 - 扩展功能(增值服务)
| 模块 | 功能 | 说明 |
|------|------|------|
| 企业出海 | CBAM解读 | 增值服务 |
| 碳能力平台 | 机构目录 | 增值服务 |
| 勋章系统 | 勋章管理 | 用户激励 |
---
## 七、接口前缀汇总
| 前缀 | 后端服务 | 用途 |
|------|---------|------|
| `/sso/*` | txw-sso | 认证服务 |
| `/mhzc/*` | txw-mhzc | 碳门户服务 |
| `/gxzx/*` | txw-gxzx | 可信碳信息服务 |
| `/yygl/*` | txw-yygl | 运营管理服务 |
| `/yhzxApi/*` | txw-mhzc | 用户中心API |
| `/ggzcApi/*` | txw-common | 公共API |
---
*文档生成时间: 2026-05-03*
*基于代码实现分析生成,排除干扰项*

View File

@ -0,0 +1,40 @@
import { fetchSso } from '@/core/request';
const basurl = '/mhzc';
export default {
// 提交反馈(新增/编辑)
submit(params) {
return fetchSso({
url: `${basurl}/yhfk/submit`,
method: 'post',
loading: true,
data: JSON.stringify(params),
});
},
// 我的反馈分页列表
myList(params) {
return fetchSso({
url: `${basurl}/yhfk/myList`,
method: 'post',
loading: true,
data: JSON.stringify(params),
});
},
// 反馈详情
detail(uuid) {
return fetchSso({
url: `${basurl}/yhfk/detail?uuid=${uuid}`,
method: 'get',
loading: true,
});
},
// 撤销反馈
cancel(uuid) {
return fetchSso({
url: `${basurl}/yhfk/cancel?uuid=${uuid}`,
method: 'get',
loading: true,
});
},
};

View File

@ -106,6 +106,11 @@ function zhanghugl() {
return import(/* webpackChunkName: "zhanghugl" */ '@/pages/index/views/yhzx/zhanghugl/index.vue'); return import(/* webpackChunkName: "zhanghugl" */ '@/pages/index/views/yhzx/zhanghugl/index.vue');
} }
// 用户反馈
function yhfk() {
return import(/* webpackChunkName: "yhfk" */ '@/pages/index/views/yhfk/index.vue');
}
// md文档查看器 // md文档查看器
function mdviewer() { function mdviewer() {
return import(/* webpackChunkName: "mdviewer" */ '@/pages/index/views/mdviewer/index.vue'); return import(/* webpackChunkName: "mdviewer" */ '@/pages/index/views/mdviewer/index.vue');
@ -188,6 +193,7 @@ export default [
{ path: 'ggwhgl', component: newsCenter, name: 'ggwhgl', meta: { title: '消息中心' } }, { path: 'ggwhgl', component: newsCenter, name: 'ggwhgl', meta: { title: '消息中心' } },
{ path: 'lsjy', component: lsjy, name: 'lsjy', meta: { title: '绿色交易' } }, { path: 'lsjy', component: lsjy, name: 'lsjy', meta: { title: '绿色交易' } },
{ path: 'zhanghugl', component: zhanghugl, name: 'zhanghugl', meta: { title: '账号管理' } }, { path: 'zhanghugl', component: zhanghugl, name: 'zhanghugl', meta: { title: '账号管理' } },
{ path: 'yhfk', component: yhfk, name: 'yhfk', meta: { title: '意见反馈' } },
] ]
}, },
{ {

View File

@ -249,12 +249,6 @@ export default {
if (this.$route.query.publish === '1') { if (this.$route.query.publish === '1') {
this.handlePublish(); this.handlePublish();
} }
// URLid
if (this.$route.query.id) {
this.$nextTick(() => {
this.scrollToItem(this.$route.query.id);
});
}
}, },
methods: { methods: {
// //
@ -332,6 +326,7 @@ export default {
async searchList() { async searchList() {
this.loading = true; this.loading = true;
this.cardList = []; this.cardList = [];
const targetId = this.$route.query.id || '';
try { try {
const prame = { const prame = {
ywlxDm: '01', ywlxDm: '01',
@ -340,6 +335,10 @@ export default {
}; };
// Y=, N= // Y=, N=
prame.scbz = this.filter.zzsscx ? 'Y' : 'N'; prame.scbz = this.filter.zzsscx ? 'Y' : 'N';
// URLidid
if (targetId) {
prame.gxUuid = targetId;
}
const { data } = await api.gxxxList(prame); const { data } = await api.gxxxList(prame);
if (data.records) { if (data.records) {
data.records.map((item) => { data.records.map((item) => {
@ -355,6 +354,10 @@ export default {
} }
this.cardList = data.records || []; this.cardList = data.records || [];
this.page.total = Number(data.total || 0); this.page.total = Number(data.total || 0);
//
if (targetId) {
this.scrollToItem(targetId);
}
} catch (error) { } catch (error) {
this.cardList = []; this.cardList = [];
this.page.total = 0; this.page.total = 0;

View File

@ -268,12 +268,6 @@ export default {
if (this.$route.query.publish === '1') { if (this.$route.query.publish === '1') {
this.handlePublish(); this.handlePublish();
} }
// URLid
if (this.$route.query.id) {
this.$nextTick(() => {
this.scrollToItem(this.$route.query.id);
});
}
}, },
methods: { methods: {
// //
@ -310,6 +304,7 @@ export default {
async searchList() { async searchList() {
this.loading = true; this.loading = true;
this.cardList = []; this.cardList = [];
const targetId = this.$route.query.id || '';
try { try {
const prame = { const prame = {
ywlxDm: '02', ywlxDm: '02',
@ -319,6 +314,10 @@ export default {
if (prame.dq === '000000') { if (prame.dq === '000000') {
prame.dq = ''; prame.dq = '';
} }
// URLidid
if (targetId) {
prame.gxUuid = targetId;
}
const { data } = await api.gxxxList(prame); const { data } = await api.gxxxList(prame);
if (data.records) { if (data.records) {
data.records.map((item) => { data.records.map((item) => {
@ -334,6 +333,10 @@ export default {
} }
this.cardList = data.records || []; this.cardList = data.records || [];
this.page.total = Number(data.total || 0); this.page.total = Number(data.total || 0);
//
if (targetId) {
this.scrollToItem(targetId);
}
} catch (error) { } catch (error) {
this.cardList = []; this.cardList = [];
this.page.total = 0; this.page.total = 0;

View File

@ -176,6 +176,20 @@ export default {
}, },
] ]
}, },
{
id: 'bzyzc',
title: '帮助与支持',
icon: '',
name: '',
children: [
{
id: 'yhfk',
title: '意见反馈',
icon: 'chat',
name: 'yhfk',
},
]
},
], ],
ejcdList: [], ejcdList: [],
iFrameSrc: '', iFrameSrc: '',

View File

@ -17,13 +17,14 @@
</template> </template>
<script> <script>
import { UploadIcon, CloudUploadIcon, EditIcon, BulletpointIcon } from 'tdesign-icons-vue'; import { UploadIcon, CloudUploadIcon, EditIcon, BulletpointIcon, ChatIcon } from 'tdesign-icons-vue';
const iconMap = { const iconMap = {
upload: UploadIcon, upload: UploadIcon,
'cloud-upload': CloudUploadIcon, 'cloud-upload': CloudUploadIcon,
edit: EditIcon, edit: EditIcon,
bulletpoint: BulletpointIcon, bulletpoint: BulletpointIcon,
chat: ChatIcon,
}; };
export default { export default {
@ -33,6 +34,7 @@ export default {
CloudUploadIcon, CloudUploadIcon,
EditIcon, EditIcon,
BulletpointIcon, BulletpointIcon,
ChatIcon,
}, },
data() { data() {
return { return {
@ -41,6 +43,7 @@ export default {
// { label: '', to: '/yhzx/tfwgj?action=publishData', icon: 'cloud-upload', bgColor: '#E6F8F0', color: '#344F3D' }, // { label: '', to: '/yhzx/tfwgj?action=publishData', icon: 'cloud-upload', bgColor: '#E6F8F0', color: '#344F3D' },
{ label: '发布需求', to: '/yhzx/tfwxq', icon: 'edit', bgColor: '#E6F8F0', color: '#344F3D' }, { label: '发布需求', to: '/yhzx/tfwxq', icon: 'edit', bgColor: '#E6F8F0', color: '#344F3D' },
// { label: '', to: '/yhzx/zzgl', icon: 'bulletpoint', bgColor: '#E6F8F0', color: '#344F3D' }, // { label: '', to: '/yhzx/zzgl', icon: 'bulletpoint', bgColor: '#E6F8F0', color: '#344F3D' },
{ label: '意见反馈', to: '/yhzx/yhfk', icon: 'chat', bgColor: '#E6F8F0', color: '#344F3D' },
], ],
}; };
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -319,7 +319,7 @@ export default {
icon: require('@/pages/index/views/home2/assets/closed-loop@2x.png') icon: require('@/pages/index/views/home2/assets/closed-loop@2x.png')
} }
], ],
hotSearchTags: ["碳核查", "ESG", "碳资产管理", "ISO 14067"], hotSearchTags: ["碳达峰", "碳核查", "ESG", "碳资产管理", "ISO 14067"],
// //
overseas2List: [ overseas2List: [
{ name: '电池法案', btnName: "申请服务", link: '', desc: "欧盟电池法案管控电池全生命周期,用碳足迹、电池护照、再生料、回收、供应链尽调设硬性准入门槛,强制绿色循环。", icon: require('@/pages/index/assets/home-dcfa-icon.png') }, { name: '电池法案', btnName: "申请服务", link: '', desc: "欧盟电池法案管控电池全生命周期,用碳足迹、电池护照、再生料、回收、供应链尽调设硬性准入门槛,强制绿色循环。", icon: require('@/pages/index/assets/home-dcfa-icon.png') },
@ -333,7 +333,7 @@ export default {
{ name: '平台公告', type: 'ptgg' } { name: '平台公告', type: 'ptgg' }
], ],
// //
partnerCount: '200+', partnerCount: ' 40+ ',
partnerList: [ partnerList: [
{ name: '合作伙伴', logo: require('@/pages/index/views/home2/assets/partner-1.png') }, { name: '合作伙伴', logo: require('@/pages/index/views/home2/assets/partner-1.png') },
{ name: '合作伙伴', logo: require('@/pages/index/views/home2/assets/partner-2.png') }, { name: '合作伙伴', logo: require('@/pages/index/views/home2/assets/partner-2.png') },
@ -554,11 +554,11 @@ export default {
}, },
getDefaultPic(type) { getDefaultPic(type) {
const defaultPics = { const defaultPics = {
gjzc: '/static_res/images/news/default-news.png', gjzc: 'static_res/images/news/default-news.png',
hyzx: '/static_res/images/news/default-hydt.png', hyzx: 'static_res/images/news/default-hyzx.png',
ptgg: '/static_res/images/news/default-ptgg.png' ptgg: 'static_res/images/news/default-ptgg.png'
}; };
return defaultPics[type] || '/static_res/images/news/default-news.png'; return defaultPics[type] || 'static_res/images/news/default-hyzx.png';
}, },
}, },
computed: { computed: {

View File

@ -69,7 +69,7 @@ export default {
display: flex; display: flex;
width: 100%; width: 100%;
height: 300px; height: 300px;
padding: 60px 300px; padding: 60px 0;
background: url(../../assets/hyzt/banner.png); background: url(../../assets/hyzt/banner.png);
background-size: cover; background-size: cover;
flex-direction: column; flex-direction: column;
@ -79,6 +79,8 @@ export default {
font-size: 64px; font-size: 64px;
font-weight: bold; font-weight: bold;
color: #000; color: #000;
margin: 0 auto;
width: 1300px;
} }
.desc { .desc {
margin-top: 10px; margin-top: 10px;

View File

@ -0,0 +1,501 @@
<template>
<div class="yhfk-page">
<div class="yhfk-page-search">
<t-form
ref="searchform"
:data="formData"
layout="inline"
@submit="onSubmit"
@reset="onReset"
>
<t-form-item label="反馈类型" name="fklx">
<t-select v-model="formData.fklx" placeholder="请选择" clearable style="width: 160px">
<t-option v-for="item in fklxOptions" :key="item.value" :value="item.value" :label="item.label" />
</t-select>
</t-form-item>
<t-form-item label="处理状态" name="czzt">
<t-select v-model="formData.czzt" placeholder="请选择" clearable style="width: 160px">
<t-option v-for="item in czztOptions" :key="item.value" :value="item.value" :label="item.label" />
</t-select>
</t-form-item>
<t-form-item label="反馈标题" name="bt">
<t-input v-model="formData.bt" placeholder="请输入" clearable style="width: 200px" />
</t-form-item>
<t-form-item label="反馈起始日期" name="lrrqq">
<t-date-picker v-model="formData.lrrqq" clearable style="width: 160px" />
</t-form-item>
<t-form-item label="反馈截止日期" name="lrrqz">
<t-date-picker v-model="formData.lrrqz" clearable style="width: 160px" />
</t-form-item>
<t-form-item>
<t-space>
<t-button theme="primary" type="submit">查询</t-button>
<t-button theme="default" type="reset">重置</t-button>
</t-space>
</t-form-item>
</t-form>
</div>
<div class="yhfk-page-table">
<div class="table-toolbar">
<t-button theme="primary" @click="openSubmit()">我要反馈</t-button>
</div>
<t-table
:data="list"
:columns="columns"
row-key="uuid"
:max-height="540"
:pagination="pagination"
@page-change="onPageChange"
:loading="tableloading"
>
<template #fklx="{ row }">
<t-tag :theme="fklxTagTheme(row.fklx)" variant="light">{{ row.fklxMc || row.fklx }}</t-tag>
</template>
<template #czzt="{ row }">
<t-tag :theme="czztTagTheme(row.czzt)" variant="light">{{ row.czztMc || row.czzt }}</t-tag>
</template>
<template #cz="{ row }">
<t-button variant="text" theme="primary" @click="openDetail(row)">查看详情</t-button>
<t-button variant="text" theme="primary" v-if="row.czzt === '0'" @click="openSubmit(row)">编辑</t-button>
<t-button variant="text" theme="danger" v-if="row.czzt === '0'" @click="onCancel(row)">撤销</t-button>
</template>
</t-table>
</div>
<!-- 提交反馈弹窗 -->
<t-dialog
:visible.sync="submitVisible"
:header="submitForm.uuid ? '编辑反馈' : '提交反馈'"
:footer="false"
:closeOnOverlayClick="false"
width="640px"
@close="closeSubmit"
>
<t-form
ref="submitFormRef"
:data="submitForm"
:rules="submitRules"
labelAlign="top"
@submit="onSave"
>
<t-form-item label="反馈类型" name="fklx">
<t-radio-group v-model="submitForm.fklx">
<t-radio v-for="item in fklxOptions" :key="item.value" :value="item.value">{{ item.label }}</t-radio>
</t-radio-group>
</t-form-item>
<t-form-item label="反馈标题" name="bt">
<t-input v-model="submitForm.bt" placeholder="请输入反馈标题不超过100字" :maxlength="100" />
</t-form-item>
<t-form-item label="反馈内容" name="nr">
<t-textarea
v-model="submitForm.nr"
placeholder="请详细描述您遇到的问题或建议不超过2000字"
:autosize="{ minRows: 5, maxRows: 10 }"
:maxlength="2000"
show-limit-number
/>
</t-form-item>
<t-form-item label="联系人" name="lxr">
<t-input v-model="submitForm.lxr" placeholder="请输入联系人姓名" />
</t-form-item>
<t-form-item label="联系电话" name="lxdh">
<t-input v-model="submitForm.lxdh" placeholder="请输入联系电话" />
</t-form-item>
<t-form-item label="联系邮箱" name="lxyx">
<t-input v-model="submitForm.lxyx" placeholder="请输入邮箱地址" />
</t-form-item>
<t-form-item>
<div style="width: 100%; text-align: center">
<t-button theme="primary" type="submit" style="margin-right: 12px">提交</t-button>
<t-button theme="default" variant="base" @click="closeSubmit">取消</t-button>
</div>
</t-form-item>
</t-form>
</t-dialog>
<!-- 反馈详情弹窗 -->
<t-dialog
:visible.sync="detailVisible"
header="反馈详情"
:footer="false"
:closeOnOverlayClick="true"
width="680px"
class="yhfk-detail-dialog"
>
<div class="detail-card" v-if="detail.uuid">
<div class="detail-header">
<div class="detail-header-top">
<t-tag :theme="fklxTagTheme(detail.fklx)" variant="light-outline" size="medium">
{{ detail.fklxMc || detail.fklx }}
</t-tag>
<t-tag :theme="czztTagTheme(detail.czzt)" variant="light" size="medium">
{{ detail.czztMc }}
</t-tag>
</div>
<div class="detail-title">{{ detail.bt }}</div>
<div class="detail-time">提交于 {{ detail.lrrq }}</div>
</div>
<div class="detail-section">
<div class="detail-section-title">反馈内容</div>
<div class="detail-content">{{ detail.nr }}</div>
</div>
<div class="detail-section">
<div class="detail-section-title">联系方式</div>
<div class="detail-grid">
<div class="detail-item">
<span class="detail-label">联系人</span>
<span class="detail-value">{{ detail.lxr || '—' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">联系电话</span>
<span class="detail-value">{{ detail.lxdh || '—' }}</span>
</div>
<div class="detail-item detail-item-full">
<span class="detail-label">联系邮箱</span>
<span class="detail-value">{{ detail.lxyx || '—' }}</span>
</div>
</div>
</div>
<div class="detail-section detail-section-result" v-if="detail.czyj || detail.czrq">
<div class="detail-section-title">处理结果</div>
<div class="detail-content" v-if="detail.czyj">{{ detail.czyj }}</div>
<div class="detail-time detail-time-end" v-if="detail.czrq">处理时间{{ detail.czrq }}</div>
</div>
</div>
</t-dialog>
</div>
</template>
<script>
import { MessagePlugin, DialogPlugin } from 'tdesign-vue';
import api from '@/pages/index/api/yhfk/index';
const FKLX_OPTIONS = [
{ value: 'bug', label: '问题反馈' },
{ value: 'suggest', label: '建议改进' },
{ value: 'function', label: '功能需求' },
{ value: 'other', label: '其他' },
];
const CZZT_OPTIONS = [
{ value: '0', label: '待处理' },
{ value: '1', label: '处理中' },
{ value: '2', label: '已处理' },
{ value: '3', label: '已关闭' },
];
export default {
name: 'YhfkPage',
data() {
return {
fklxOptions: FKLX_OPTIONS,
czztOptions: CZZT_OPTIONS,
formData: {
fklx: '',
czzt: '',
bt: '',
lrrqq: '',
lrrqz: '',
},
list: [],
tableloading: false,
pagination: {
current: 1,
pageSize: 10,
total: 0,
},
columns: [
{ align: 'center', colKey: 'serial-number', width: '70px', title: '序号' },
{ align: 'left', width: '120px', colKey: 'fklx', title: '反馈类型' },
{ align: 'left', minWidth: '200px', colKey: 'bt', title: '反馈标题', ellipsis: true },
{ align: 'left', width: '160px', colKey: 'lrrq', title: '提交时间' },
{ align: 'left', width: '120px', colKey: 'czzt', title: '处理状态' },
{ align: 'left', width: '180px', colKey: 'cz', title: '操作', fixed: 'right' },
],
submitVisible: false,
submitForm: this.defaultSubmitForm(),
submitRules: {
fklx: [{ required: true, message: '请选择反馈类型', type: 'error' }],
bt: [{ required: true, message: '请输入反馈标题', type: 'error' }],
nr: [{ required: true, message: '请输入反馈内容', type: 'error' }],
lxdh: [
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入有效的手机号',
type: 'warning',
trigger: 'blur',
},
],
lxyx: [
{
pattern: /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/,
message: '请输入有效的邮箱地址',
type: 'warning',
trigger: 'blur',
},
],
},
detailVisible: false,
detail: {},
};
},
created() {
this.onQuery();
},
methods: {
defaultSubmitForm() {
return {
uuid: '',
fklx: 'bug',
bt: '',
nr: '',
lxr: '',
lxdh: '',
lxyx: '',
};
},
fklxTagTheme(fklx) {
const map = { bug: 'danger', suggest: 'primary', function: 'success', other: 'default' };
return map[fklx] || 'default';
},
czztTagTheme(czzt) {
const map = { 0: 'warning', 1: 'primary', 2: 'success', 3: 'default' };
return map[czzt] || 'default';
},
onSubmit() {
this.pagination.current = 1;
this.onQuery();
},
onReset() {
this.formData = { fklx: '', czzt: '', bt: '', lrrqq: '', lrrqz: '' };
this.pagination.current = 1;
this.onQuery();
},
onPageChange(pageInfo) {
this.pagination.current = pageInfo.current;
this.pagination.pageSize = pageInfo.pageSize;
this.onQuery();
},
async onQuery() {
this.tableloading = true;
const params = {
...this.formData,
pageNum: this.pagination.current,
pageSize: this.pagination.pageSize,
};
try {
const res = await api.myList(params);
this.list = res?.data?.records || [];
this.pagination.total = Number(res?.data?.total || 0);
} catch (err) {
this.list = [];
this.pagination.total = 0;
} finally {
this.tableloading = false;
}
},
openSubmit(row) {
if (row && row.uuid) {
this.submitForm = {
uuid: row.uuid,
fklx: row.fklx,
bt: row.bt,
nr: row.nr,
lxr: row.lxr || '',
lxdh: row.lxdh || '',
lxyx: row.lxyx || '',
};
} else {
this.submitForm = this.defaultSubmitForm();
}
this.submitVisible = true;
},
closeSubmit() {
this.submitVisible = false;
this.submitForm = this.defaultSubmitForm();
},
async onSave({ validateResult }) {
if (validateResult !== true) return;
try {
const res = await api.submit(this.submitForm);
if (res?.code === 1 || res?.code === 0) {
MessagePlugin.success(this.submitForm.uuid ? '修改成功' : '提交成功');
this.closeSubmit();
this.onQuery();
}
} catch (err) {
//
}
},
async openDetail(row) {
try {
const res = await api.detail(row.uuid);
this.detail = res?.data || {};
this.detailVisible = true;
} catch (err) {
// ignore
}
},
onCancel(row) {
const dialog = DialogPlugin.confirm({
header: '提示',
body: '确认撤销该条反馈吗?撤销后无法恢复。',
onConfirm: async () => {
try {
const res = await api.cancel(row.uuid);
if (res?.code === 1 || res?.code === 0) {
MessagePlugin.success('撤销成功');
this.onQuery();
}
} finally {
dialog.destroy();
}
},
onClose: () => dialog.destroy(),
});
},
},
};
</script>
<style lang="less" scoped>
.yhfk-page {
height: 100%;
padding: 24px;
background: #ecf1f9;
}
.yhfk-page-search {
padding: 16px 24px;
background: white;
border-radius: 4px;
}
.yhfk-page-table {
margin-top: 16px;
padding: 16px;
background: white;
border-radius: 4px;
}
.table-toolbar {
margin-bottom: 12px;
}
.detail-box {
padding: 8px 0;
}
::v-deep .yhfk-detail-dialog .t-dialog__body {
padding: 0;
}
::v-deep .yhfk-detail-dialog .t-dialog__body > div {
padding: 0;
}
.detail-card {
padding: 24px 32px;
}
.detail-header {
margin-bottom: 24px;
padding-bottom: 20px;
border-bottom: 1px solid #e7e7e7;
}
.detail-header-top {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
}
.detail-title {
font-size: 18px;
font-weight: 600;
color: #1d2129;
line-height: 1.5;
word-break: break-all;
}
.detail-time {
font-size: 13px;
color: #86909c;
margin-top: 8px;
}
.detail-section {
margin-bottom: 24px;
}
.detail-section-title {
font-size: 14px;
font-weight: 600;
color: #1d2129;
margin-bottom: 12px;
padding-left: 10px;
position: relative;
}
.detail-section-title::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 14px;
background: #0052d9;
border-radius: 2px;
}
.detail-content {
background: #f7f8fa;
border-radius: 6px;
padding: 14px 16px;
font-size: 14px;
line-height: 1.8;
color: #4e5969;
white-space: pre-wrap;
word-break: break-all;
}
.detail-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.detail-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.detail-item-full {
grid-column: 1 / -1;
}
.detail-label {
font-size: 13px;
color: #86909c;
}
.detail-value {
font-size: 14px;
color: #1d2129;
font-weight: 500;
word-break: break-all;
}
.detail-section-result .detail-content {
background: #f2f8ff;
border-left: 3px solid #0052d9;
}
.detail-time-end {
margin-top: 12px;
text-align: right;
}
</style>

View File

@ -287,20 +287,20 @@ module.exports = {
// 会误伤 SPA 路由 /view/mhzc/...,刷新时整页请求被转发到后端导致 Proxy error。必须用 ^ 限定为路径前缀。 // 会误伤 SPA 路由 /view/mhzc/...,刷新时整页请求被转发到后端导致 Proxy error。必须用 ^ 限定为路径前缀。
proxy: { proxy: {
'^/sso': { '^/sso': {
// target: 'http://localhost:9301', target: 'http://localhost:9300',
target: 'http://carbon.liantu.tech', // target: 'http://carbon.liantu.tech',
// target: 'http://10.23.20.13:94/', // target: 'http://10.23.20.13:94/',
changeOrigin: true, changeOrigin: true,
}, },
'^/mhzc': { '^/mhzc': {
// target: 'http://localhost:9302', target: 'http://localhost:9300',
target: 'http://carbon.liantu.tech', // target: 'http://carbon.liantu.tech',
// target: 'http://10.23.20.13:94/', // target: 'http://10.23.20.13:94/',
changeOrigin: true, changeOrigin: true,
}, },
'^/gxzx': { '^/gxzx': {
// target: 'http://localhost:9303', target: 'http://localhost:9300',
target: 'http://carbon.liantu.tech', // target: 'http://carbon.liantu.tech',
// target: 'http://10.23.20.13:94/', // target: 'http://10.23.20.13:94/',
changeOrigin: true, changeOrigin: true,
}, },

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,26 @@
-- 用户反馈表
CREATE TABLE `txw_mhzc_yhfkb` (
`uuid` VARCHAR(50) NOT NULL COMMENT 'UUID',
`fklx` VARCHAR(20) NOT NULL COMMENT '反馈类型bug=问题反馈、suggest=建议改进、function=功能需求、other=其他)',
`fklx_mc` VARCHAR(50) DEFAULT NULL COMMENT '反馈类型名称',
`bt` VARCHAR(200) NOT NULL COMMENT '反馈标题',
`nr` TEXT COMMENT '反馈内容',
`tp` VARCHAR(2000) DEFAULT NULL COMMENT '附件图片(多个用逗号分隔)',
`lxr` VARCHAR(50) DEFAULT NULL COMMENT '联系人',
`lxdh` VARCHAR(50) DEFAULT NULL COMMENT '联系电话',
`lxyx` VARCHAR(100) DEFAULT NULL COMMENT '联系邮箱',
`czzt` CHAR(1) DEFAULT '0' COMMENT '处理状态0=待处理、1=处理中、2=已处理、3=已关闭)',
`czyj` TEXT COMMENT '处理意见',
`czrq` DATETIME DEFAULT NULL COMMENT '处理日期',
`czruuid` VARCHAR(50) DEFAULT NULL COMMENT '处理人UUID',
`qyuuid` VARCHAR(50) DEFAULT NULL COMMENT '企业UUID',
`qymc` VARCHAR(200) DEFAULT NULL COMMENT '企业名称',
`lrrq` DATETIME DEFAULT NULL COMMENT '录入日期',
`lrruuid` VARCHAR(50) DEFAULT NULL COMMENT '录入人UUID',
`xgrq` DATETIME DEFAULT NULL COMMENT '修改日期',
`xgruuid` VARCHAR(50) DEFAULT NULL COMMENT '修改人UUID',
PRIMARY KEY (`uuid`),
KEY `idx_lrruuid` (`lrruuid`),
KEY `idx_czzt` (`czzt`),
KEY `idx_lrrq` (`lrrq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户反馈表';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
package com.css.txw.mhzc.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.txw.mhzc.pojo.vo.YhfkReqVO;
import com.css.txw.mhzc.pojo.vo.YhfkVO;
import com.css.txw.mhzc.service.TxwMhzcYhfkbService;
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;
@Tag(name = "用户反馈接口")
@RestController
@RequestMapping("/yhfk")
@Validated
@Slf4j
public class YhfkController {
@Resource
private TxwMhzcYhfkbService yhfkbService;
@PostMapping("/submit")
@Operation(summary = "提交反馈", description = "新增或编辑(仅未处理状态可编辑)")
public CommonResult<String> submit(@RequestBody YhfkVO reqVO) {
return CommonResult.success(yhfkbService.submit(reqVO));
}
@PostMapping("/myList")
@Operation(summary = "我的反馈分页列表", description = "分页查询当前登录用户提交的反馈记录")
public CommonResult<Page<YhfkVO>> myList(@RequestBody YhfkReqVO reqVO) {
return CommonResult.success(yhfkbService.myFkPage(reqVO));
}
@GetMapping("/detail")
@Operation(summary = "反馈详情", description = "根据UUID查询反馈详情")
public CommonResult<YhfkVO> detail(@RequestParam("uuid") String uuid) {
return CommonResult.success(yhfkbService.getDetail(uuid));
}
@GetMapping("/cancel")
@Operation(summary = "撤销反馈", description = "撤销自己提交的、且未处理的反馈")
public CommonResult<String> cancel(@RequestParam("uuid") String uuid) {
return CommonResult.success(yhfkbService.cancel(uuid));
}
}

View File

@ -28,6 +28,7 @@ public interface TxwGxzxGxxxbMapper extends BaseMapper<TxwGxzxGxxxbDO> {
.and(!GyUtils.isNull(reqVO.getNr()),w->w.like(TxwGxzxGxxxbDO::getBt1,reqVO.getNr()).or().like(TxwGxzxGxxxbDO::getFwnr,reqVO.getNr()).or().like(TxwGxzxGxxxbDO::getQymc,reqVO.getNr())) .and(!GyUtils.isNull(reqVO.getNr()),w->w.like(TxwGxzxGxxxbDO::getBt1,reqVO.getNr()).or().like(TxwGxzxGxxxbDO::getFwnr,reqVO.getNr()).or().like(TxwGxzxGxxxbDO::getQymc,reqVO.getNr()))
.eq(TxwGxzxGxxxbDO::getSjzt,"Y") .eq(TxwGxzxGxxxbDO::getSjzt,"Y")
.eq(TxwGxzxGxxxbDO::getZt,"3") .eq(TxwGxzxGxxxbDO::getZt,"3")
.eq(!GyUtils.isNull(reqVO.getGxUuid()),TxwGxzxGxxxbDO::getGxUuid,reqVO.getGxUuid())
.in(!GyUtils.isNull(reqVO.getSshy()),TxwGxzxGxxxbDO::getSshy,reqVO.getSshy()) .in(!GyUtils.isNull(reqVO.getSshy()),TxwGxzxGxxxbDO::getSshy,reqVO.getSshy())
.like(!GyUtils.isNull(reqVO.getDq()),TxwGxzxGxxxbDO::getFwfw,reqVO.getDq()) .like(!GyUtils.isNull(reqVO.getDq()),TxwGxzxGxxxbDO::getFwfw,reqVO.getDq())
.lt(!GyUtils.isNull(reqVO.getFbsjz()),TxwGxzxGxxxbDO::getLrrq,reqVO.getFbsjz()) .lt(!GyUtils.isNull(reqVO.getFbsjz()),TxwGxzxGxxxbDO::getLrrq,reqVO.getFbsjz())

View File

@ -0,0 +1,43 @@
package com.css.txw.mhzc.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.txw.mhzc.pojo.domain.TxwMhzcYhfkbDO;
import com.css.txw.mhzc.pojo.vo.YhfkReqVO;
/**
* @author Claude
* @description 针对表txw_mhzc_yhfkb(用户反馈表)的数据库操作Mapper
*/
public interface TxwMhzcYhfkbMapper extends BaseMapper<TxwMhzcYhfkbDO> {
/**
* 分页查询用户反馈列表
*/
default Page<TxwMhzcYhfkbDO> getYhfkPage(YhfkReqVO reqVO, String yhUuid) {
QueryWrapper<TxwMhzcYhfkbDO> wrapper = new QueryWrapper<>();
if (!GyUtils.isNull(yhUuid)) {
wrapper.lambda().eq(TxwMhzcYhfkbDO::getLrruuid, yhUuid);
}
if (!GyUtils.isNull(reqVO.getFklx())) {
wrapper.lambda().eq(TxwMhzcYhfkbDO::getFklx, reqVO.getFklx());
}
if (!GyUtils.isNull(reqVO.getCzzt())) {
wrapper.lambda().eq(TxwMhzcYhfkbDO::getCzzt, reqVO.getCzzt());
}
if (!GyUtils.isNull(reqVO.getBt())) {
wrapper.lambda().like(TxwMhzcYhfkbDO::getBt, reqVO.getBt());
}
if (!GyUtils.isNull(reqVO.getLrrqq())) {
wrapper.lambda().ge(TxwMhzcYhfkbDO::getLrrq, reqVO.getLrrqq());
}
if (!GyUtils.isNull(reqVO.getLrrqz())) {
wrapper.lambda().le(TxwMhzcYhfkbDO::getLrrq, reqVO.getLrrqz() + " 23:59:59");
}
wrapper.lambda().orderByDesc(TxwMhzcYhfkbDO::getLrrq);
Page<TxwMhzcYhfkbDO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
return selectPage(page, wrapper);
}
}

View File

@ -0,0 +1,135 @@
package com.css.txw.mhzc.pojo.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 用户反馈表
* @TableName txw_mhzc_yhfkb
*/
@TableName(value = "txw_mhzc_yhfkb")
@Data
public class TxwMhzcYhfkbDO implements Serializable {
/**
* UUID
*/
@TableId(value = "uuid")
private String uuid;
/**
* 反馈类型bug=问题反馈suggest=建议改进function=功能需求other=其他
*/
@TableField(value = "fklx")
private String fklx;
/**
* 反馈类型名称
*/
@TableField(value = "fklx_mc")
private String fklxMc;
/**
* 反馈标题
*/
@TableField(value = "bt")
private String bt;
/**
* 反馈内容
*/
@TableField(value = "nr")
private String nr;
/**
* 附件图片多个用逗号分隔
*/
@TableField(value = "tp")
private String tp;
/**
* 联系人
*/
@TableField(value = "lxr")
private String lxr;
/**
* 联系电话
*/
@TableField(value = "lxdh")
private String lxdh;
/**
* 联系邮箱
*/
@TableField(value = "lxyx")
private String lxyx;
/**
* 处理状态0=待处理1=处理中2=已处理3=已关闭
*/
@TableField(value = "czzt")
private String czzt;
/**
* 处理意见
*/
@TableField(value = "czyj")
private String czyj;
/**
* 处理日期
*/
@TableField(value = "czrq")
private Date czrq;
/**
* 处理人UUID
*/
@TableField(value = "czruuid")
private String czruuid;
/**
* 企业UUID
*/
@TableField(value = "qyuuid")
private String qyuuid;
/**
* 企业名称
*/
@TableField(value = "qymc")
private String qymc;
/**
* 录入日期
*/
@TableField(value = "lrrq")
private Date lrrq;
/**
* 录入人UUID
*/
@TableField(value = "lrruuid")
private String lrruuid;
/**
* 修改日期
*/
@TableField(value = "xgrq")
private Date xgrq;
/**
* 修改人UUID
*/
@TableField(value = "xgruuid")
private String xgruuid;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -23,6 +23,9 @@ public class GxxxReqVO extends PageParam implements Serializable{
//收藏标志 //收藏标志
private String scbz; private String scbz;
//供需UUID按ID精确查询单条
private String gxUuid;
//地区 //地区
private String dq; private String dq;
// //

View File

@ -0,0 +1,36 @@
package com.css.txw.mhzc.pojo.vo;
import com.css.ggzc.framework.common.pojo.PageParam;
import lombok.Data;
import java.io.Serializable;
@Data
public class YhfkReqVO extends PageParam implements Serializable {
/**
* 反馈类型bug=问题反馈suggest=建议改进function=功能需求other=其他
*/
private String fklx;
/**
* 处理状态0=待处理1=处理中2=已处理3=已关闭
*/
private String czzt;
/**
* 反馈标题模糊查询
*/
private String bt;
/**
* 反馈起始日期yyyy-MM-dd
*/
private String lrrqq;
/**
* 反馈截止日期yyyy-MM-dd
*/
private String lrrqz;
}

View File

@ -0,0 +1,97 @@
package com.css.txw.mhzc.pojo.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class YhfkVO implements Serializable {
private String uuid;
/**
* 反馈类型
*/
private String fklx;
/**
* 反馈类型名称
*/
private String fklxMc;
/**
* 反馈标题
*/
private String bt;
/**
* 反馈内容
*/
private String nr;
/**
* 附件图片多个用逗号分隔
*/
private String tp;
/**
* 联系人
*/
private String lxr;
/**
* 联系电话
*/
private String lxdh;
/**
* 联系邮箱
*/
private String lxyx;
/**
* 处理状态0=待处理1=处理中2=已处理3=已关闭
*/
private String czzt;
/**
* 处理状态名称
*/
private String czztMc;
/**
* 处理意见
*/
private String czyj;
/**
* 处理日期
*/
private String czrq;
/**
* 处理人UUID
*/
private String czruuid;
/**
* 企业UUID
*/
private String qyuuid;
/**
* 企业名称
*/
private String qymc;
/**
* 录入日期
*/
private String lrrq;
/**
* 录入人UUID
*/
private String lrruuid;
}

View File

@ -0,0 +1,35 @@
package com.css.txw.mhzc.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.css.txw.mhzc.pojo.domain.TxwMhzcYhfkbDO;
import com.css.txw.mhzc.pojo.vo.YhfkReqVO;
import com.css.txw.mhzc.pojo.vo.YhfkVO;
/**
* @author Claude
* @description 针对表txw_mhzc_yhfkb(用户反馈表)的数据库操作Service
*/
public interface TxwMhzcYhfkbService extends IService<TxwMhzcYhfkbDO> {
/**
* 提交反馈
*/
String submit(YhfkVO yhfkVO);
/**
* 撤销反馈仅限当前用户且未处理
*/
String cancel(String uuid);
/**
* 当前用户反馈分页列表
*/
Page<YhfkVO> myFkPage(YhfkReqVO reqVO);
/**
* 反馈详情
*/
YhfkVO getDetail(String uuid);
}

View File

@ -107,7 +107,7 @@ public class SearchServiceImpl implements SearchService {
@Override @Override
public List<String> getHotSearchKeywords() { public List<String> getHotSearchKeywords() {
return Arrays.asList("核查", "ESG", "碳资产管理", "ISO 14067"); return Arrays.asList("达峰", "核查", "ESG", "碳资产管理", "ISO 14067");
} }
@Override @Override

View File

@ -10,6 +10,7 @@ import com.css.txw.mhzc.service.TxwMhzcHydtbService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -26,18 +27,20 @@ public class TxwMhzcHydtbServiceImpl extends ServiceImpl<TxwMhzcHydtbMapper, Txw
@Resource @Resource
private TxwMhzcHydtbMapper hydtbMapper; private TxwMhzcHydtbMapper hydtbMapper;
private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override @Override
public List<HydtbVO> getByLx(String lx) { public List<HydtbVO> getByLx(String lx) {
List<TxwMhzcHydtbDO> list = hydtbMapper.getByLx(lx); List<TxwMhzcHydtbDO> list = hydtbMapper.getByLx(lx);
if (GyUtils.isNull(list)) return null; if (GyUtils.isNull(list)) return null;
return BeanUtils.toBean(list, HydtbVO.class); return list.stream().map(this::toVO).collect(Collectors.toList());
} }
@Override @Override
public List<HydtbVO> getAll() { public List<HydtbVO> getAll() {
List<TxwMhzcHydtbDO> list = hydtbMapper.getAllOrderByFbsj(); List<TxwMhzcHydtbDO> list = hydtbMapper.getAllOrderByFbsj();
if (GyUtils.isNull(list)) return null; if (GyUtils.isNull(list)) return null;
return BeanUtils.toBean(list, HydtbVO.class); return list.stream().map(this::toVO).collect(Collectors.toList());
} }
@Override @Override
@ -46,4 +49,12 @@ public class TxwMhzcHydtbServiceImpl extends ServiceImpl<TxwMhzcHydtbMapper, Txw
if (GyUtils.isNull(all)) return null; if (GyUtils.isNull(all)) return null;
return all.stream().collect(Collectors.groupingBy(HydtbVO::getLx)); return all.stream().collect(Collectors.groupingBy(HydtbVO::getLx));
} }
private HydtbVO toVO(TxwMhzcHydtbDO hydtbDO) {
HydtbVO vo = BeanUtils.toBean(hydtbDO, HydtbVO.class);
if (hydtbDO.getFbsj() != null) {
vo.setFbsj(hydtbDO.getFbsj().format(DTF));
}
return vo;
}
} }

View File

@ -0,0 +1,144 @@
package com.css.txw.mhzc.service.impl;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.css.ggzc.framework.common.exception.ServiceException;
import com.css.ggzc.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.ggzc.framework.common.util.object.BeanUtils;
import com.css.ggzc.framework.session.SessionUtils;
import com.css.txw.mhzc.mapper.TxwMhzcYhfkbMapper;
import com.css.txw.mhzc.pojo.domain.TxwMhzcYhfkbDO;
import com.css.txw.mhzc.pojo.vo.YhfkReqVO;
import com.css.txw.mhzc.pojo.vo.YhfkVO;
import com.css.txw.mhzc.service.TxwMhzcYhfkbService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author Claude
* @description 针对表txw_mhzc_yhfkb(用户反馈表)的数据库操作Service实现
*/
@Service
public class TxwMhzcYhfkbServiceImpl extends ServiceImpl<TxwMhzcYhfkbMapper, TxwMhzcYhfkbDO>
implements TxwMhzcYhfkbService {
@Resource
private TxwMhzcYhfkbMapper yhfkbMapper;
private static final Map<String, String> FKLX_MAP = new HashMap<>();
private static final Map<String, String> CZZT_MAP = new HashMap<>();
static {
FKLX_MAP.put("bug", "问题反馈");
FKLX_MAP.put("suggest", "建议改进");
FKLX_MAP.put("function", "功能需求");
FKLX_MAP.put("other", "其他");
CZZT_MAP.put("0", "待处理");
CZZT_MAP.put("1", "处理中");
CZZT_MAP.put("2", "已处理");
CZZT_MAP.put("3", "已关闭");
}
@Override
public String submit(YhfkVO yhfkVO) {
if (GyUtils.isNull(yhfkVO.getFklx())) {
throw new ServiceException(GlobalErrorCodeConstants.ERROR.getCode(), "反馈类型不能为空");
}
if (GyUtils.isNull(yhfkVO.getBt())) {
throw new ServiceException(GlobalErrorCodeConstants.ERROR.getCode(), "反馈标题不能为空");
}
if (GyUtils.isNull(yhfkVO.getNr())) {
throw new ServiceException(GlobalErrorCodeConstants.ERROR.getCode(), "反馈内容不能为空");
}
TxwMhzcYhfkbDO yhfkbDO = BeanUtils.toBean(yhfkVO, TxwMhzcYhfkbDO.class);
Date now = new Date();
if (GyUtils.isNull(yhfkVO.getUuid())) {
yhfkbDO.setUuid(IdUtil.fastSimpleUUID());
yhfkbDO.setFklxMc(FKLX_MAP.getOrDefault(yhfkVO.getFklx(), ""));
yhfkbDO.setCzzt("0");
yhfkbDO.setQyuuid(SessionUtils.getQyuuid());
yhfkbDO.setLrrq(now);
yhfkbDO.setLrruuid(SessionUtils.getYhUuid());
this.save(yhfkbDO);
} else {
TxwMhzcYhfkbDO old = this.getById(yhfkVO.getUuid());
if (old == null) {
throw new ServiceException(GlobalErrorCodeConstants.ERROR.getCode(), "反馈不存在");
}
if (!"0".equals(old.getCzzt())) {
throw new ServiceException(GlobalErrorCodeConstants.ERROR.getCode(), "已受理的反馈无法修改");
}
yhfkbDO.setFklxMc(FKLX_MAP.getOrDefault(yhfkVO.getFklx(), ""));
yhfkbDO.setXgrq(now);
yhfkbDO.setXgruuid(SessionUtils.getYhUuid());
this.updateById(yhfkbDO);
}
return yhfkbDO.getUuid();
}
@Override
public String cancel(String uuid) {
TxwMhzcYhfkbDO yhfkbDO = this.getById(uuid);
if (yhfkbDO == null) {
throw new ServiceException(GlobalErrorCodeConstants.ERROR.getCode(), "反馈不存在");
}
if (!SessionUtils.getYhUuid().equals(yhfkbDO.getLrruuid())) {
throw new ServiceException(GlobalErrorCodeConstants.ERROR.getCode(), "无权撤销他人反馈");
}
if (!"0".equals(yhfkbDO.getCzzt())) {
throw new ServiceException(GlobalErrorCodeConstants.ERROR.getCode(), "已受理的反馈无法撤销");
}
this.removeById(uuid);
return "success";
}
@Override
public Page<YhfkVO> myFkPage(YhfkReqVO reqVO) {
String yhUuid = SessionUtils.getYhUuid();
Page<TxwMhzcYhfkbDO> doPage = yhfkbMapper.getYhfkPage(reqVO, yhUuid);
Page<YhfkVO> voPage = new Page<>();
voPage.setCurrent(doPage.getCurrent());
voPage.setSize(doPage.getSize());
voPage.setTotal(doPage.getTotal());
if (GyUtils.isNull(doPage.getRecords())) {
return voPage;
}
List<YhfkVO> records = doPage.getRecords().stream().map(this::toVO).collect(Collectors.toList());
voPage.setRecords(records);
return voPage;
}
@Override
public YhfkVO getDetail(String uuid) {
TxwMhzcYhfkbDO yhfkbDO = this.getById(uuid);
if (yhfkbDO == null) return null;
return toVO(yhfkbDO);
}
private static final String DATE_FMT = "yyyy-MM-dd HH:mm:ss";
private YhfkVO toVO(TxwMhzcYhfkbDO yhfkbDO) {
YhfkVO vo = BeanUtils.toBean(yhfkbDO, YhfkVO.class);
vo.setFklxMc(FKLX_MAP.getOrDefault(yhfkbDO.getFklx(), yhfkbDO.getFklxMc()));
vo.setCzztMc(CZZT_MAP.getOrDefault(yhfkbDO.getCzzt(), ""));
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FMT);
if (yhfkbDO.getLrrq() != null) {
vo.setLrrq(sdf.format(yhfkbDO.getLrrq()));
}
if (yhfkbDO.getCzrq() != null) {
vo.setCzrq(sdf.format(yhfkbDO.getCzrq()));
}
return vo;
}
}