diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..90fb244 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(openspec archive add-loan-pricing-frontend:*)" + ] + } +} diff --git a/.claude/settings.local.json b/.claude/settings.local.json index d068b80..1d3ec5e 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -11,7 +11,9 @@ "Bash(findstr:*)", "Bash(chcp:*)", "Bash(cmd.exe:*)", - "Bash(powershell -Command:*)" + "Bash(powershell -Command:*)", + "Bash(openspec archive add-loan-pricing-create:*)", + "Bash(git add:*)" ], "additionalDirectories": [ "d:\\利率定价\\loan-pricing-892\\loan-pricing-892-v2.0" diff --git a/openspec/changes/archive/2026-01-20-add-loan-pricing-create/proposal.md b/openspec/changes/archive/2026-01-20-add-loan-pricing-create/proposal.md new file mode 100644 index 0000000..8758a37 --- /dev/null +++ b/openspec/changes/archive/2026-01-20-add-loan-pricing-create/proposal.md @@ -0,0 +1,33 @@ +# Change: 添加利率定价流程创建功能 + +## Why + +当前 `add-loan-pricing-frontend` 变更已实现流程列表查询和详情查看功能,但缺少创建新流程的能力。业务人员需要能够通过 Web 界面发起新的利率定价申请,而不是直接调用后端 API。 + +## What Changes + +- 修改前端页面组件 `ruoyi-ui/src/views/loanPricing/workflow/index.vue`: + - 添加"新增"按钮(带权限控制 `loanPricing:workflow:create`) + - 添加创建流程表单对话框 + - 实现表单验证逻辑 + - 实现表单提交功能 + +- 新增前端 API 接口函数: + - `createWorkflow(data)` - 创建利率定价流程 + +- 新增数据库菜单权限: + - 添加创建权限 `loanPricing:workflow:create` + +## Impact + +- **Affected specs:** 修改 `loan-pricing-workflow-ui` 能力规格 +- **Affected code:** + - **修改 `ruoyi-ui/src/api/loanPricing/workflow.js`** - 新增创建接口 + - **修改 `ruoyi-ui/src/views/loanPricing/workflow/index.vue`** - 添加创建功能 + - **修改 `sys_menu` 表** - 添加创建权限按钮 + +## Dependencies + +- 依赖已完成的 `add-loan-pricing-workflow` 后端变更(提供创建 API) +- 依赖已完成的 `add-loan-pricing-frontend` 前端变更(提供列表和详情页面) +- 后端 API 接口文档位于 `doc/api/loan-pricing-workflow-api.md`(接口1:发起利率定价流程) diff --git a/openspec/changes/archive/2026-01-20-add-loan-pricing-create/specs/loan-pricing-workflow-ui/spec.md b/openspec/changes/archive/2026-01-20-add-loan-pricing-create/specs/loan-pricing-workflow-ui/spec.md new file mode 100644 index 0000000..dd6fd84 --- /dev/null +++ b/openspec/changes/archive/2026-01-20-add-loan-pricing-create/specs/loan-pricing-workflow-ui/spec.md @@ -0,0 +1,94 @@ +# Capability: loan-pricing-workflow-ui + +利率定价流程前端用户界面能力。 + +## MODIFIED Requirements + +### Requirement: 流程列表查询 + +系统 SHALL 提供利率定价流程列表查询页面,支持分页和多条件筛选,并支持创建新流程。 + +#### Scenario: 查询流程列表 +- **WHEN** 用户已登录系统且具有 `loanPricing:workflow:list` 权限,访问"利率定价管理 > 流程列表"菜单 +- **THEN** 系统显示利率定价流程列表页面,包含查询表单(客户名称模糊查询、创建者、机构号筛选)、搜索和重置按钮、新增按钮(带权限控制)、数据表格(业务方流水号、客户名称、客户类型、担保方式、申请金额、贷款利率、创建时间、创建者)、分页组件、操作列(包含"查看"按钮) + +#### Scenario: 使用筛选条件查询 +- **WHEN** 用户在流程列表页面输入客户名称或选择创建者/机构号,点击搜索按钮 +- **THEN** 系统根据筛选条件查询并更新列表数据 + +#### Scenario: 重置筛选条件 +- **WHEN** 用户已设置筛选条件,点击重置按钮 +- **THEN** 系统清空所有筛选条件并重新查询全部数据 + +### Requirement: 流程详情查看 + +系统 SHALL 提供流程详情查看功能,以对话框形式展示完整的流程信息。 + +#### Scenario: 查看流程详情 +- **WHEN** 用户在流程列表页面且具有 `loanPricing:workflow:query` 权限,点击列表中某条记录的"查看"按钮 +- **THEN** 系统弹出详情对话框,展示完整的流程信息(基本信息:业务方流水号、机构编码、客户内码、客户名称、证件类型;业务信息:客户类型、担保方式、申请金额、贷款利率、贷款用途;业务标识:中间业务标识、企业标识;抵质押信息:抵质押类型、是否三方所有;其他信息:创建时间、创建者、更新时间、更新者) + +### Requirement: 菜单和权限配置 + +系统 SHALL 在数据库中正确配置菜单项和权限,确保用户可以访问功能。 + +#### Scenario: 菜单显示和导航 +- **WHEN** 用户已登录系统且具有利率定价流程相关权限 +- **THEN** 系统在左侧菜单栏显示"利率定价管理"一级菜单,展开后显示"流程列表"二级菜单项 + +#### Scenario: 菜单路由配置 +- **WHEN** 用户点击"流程列表"菜单项,系统处理路由跳转 +- **THEN** 系统导航至 `/loanPricing/workflow` 路径,加载对应的前端组件 + +### Requirement: API 接口集成 + +前端 SHALL 正确调用后端 API 接口获取数据和提交创建请求。 + +#### Scenario: 列表接口调用 +- **WHEN** 用户访问流程列表页面,页面初始化或用户执行查询操作 +- **THEN** 前端调用 `GET /loanPricing/workflow/list` 接口,传入分页和筛选参数 + +#### Scenario: 详情接口调用 +- **WHEN** 用户点击查看按钮,前端获取选中记录的业务方流水号 +- **THEN** 前端调用 `GET /loanPricing/workflow/{serialNum}` 接口获取详情数据 + +#### Scenario: 创建接口调用 +- **WHEN** 用户填写完创建表单并点击确定按钮 +- **THEN** 前端调用 `POST /loanPricing/workflow/create` 接口,传入表单数据,成功后关闭对话框并刷新列表 + +## ADDED Requirements + +### Requirement: 流程创建 + +系统 SHALL 提供创建新利率定价流程的功能,通过表单对话框收集必要信息。 + +#### Scenario: 打开创建表单 +- **WHEN** 用户在流程列表页面且具有 `loanPricing:workflow:create` 权限,点击"新增"按钮 +- **THEN** 系统弹出创建流程表单对话框,显示所有必填和可选字段 + +#### Scenario: 表单字段显示 +- **WHEN** 用户打开创建流程表单对话框 +- **THEN** 系统显示以下字段分组: + - 基本信息:客户内码(必填)、客户名称、客户类型(必填,下拉选择:个人/企业)、证件类型 + - 贷款信息:申请金额(必填)、贷款利率(必填)、担保方式(必填,下拉选择:信用/保证/抵押/质押)、贷款用途(下拉选择:consumer/business) + - 中间业务标识(个人):个人快捷支付(开关)、个人电费代扣(开关) + - 中间业务标识(企业):企业电费代扣(开关)、企业水费代扣(开关) + - 企业标识:净身企业(开关)、开立基本结算账户(开关)、制造业企业(开关)、省农担担保贷款(开关)、纳税信用等级A级(开关)、县级及以上农业龙头企业(开关)、普惠小微借款人(开关) + - 抵质押信息:抵质押类型(下拉选择:一线/一类/二类)、抵质押物三方所有(开关)、是否有经营佐证(开关) + - 固定字段:机构编码(隐藏,固定值931000)、运行模式(隐藏,固定值1) + +#### Scenario: 表单验证 +- **WHEN** 用户填写表单并点击确定按钮 +- **THEN** 系统验证必填字段:客户内码、客户类型、担保方式、申请金额、贷款利率,如有缺失则显示错误提示 + +#### Scenario: 提交创建成功 +- **WHEN** 用户填写完必填字段并点击确定按钮,后端返回成功响应 +- **THEN** 系统关闭对话框,显示成功提示消息,刷新列表数据 + +#### Scenario: 取消创建 +- **WHEN** 用户点击取消按钮或对话框关闭按钮 +- **THEN** 系统关闭对话框,不保存数据,不刷新列表 + +#### Scenario: 新增按钮权限控制 +- **WHEN** 用户不具有 `loanPricing:workflow:create` 权限 +- **THEN** 系统不显示"新增"按钮 diff --git a/openspec/changes/archive/2026-01-20-add-loan-pricing-create/tasks.md b/openspec/changes/archive/2026-01-20-add-loan-pricing-create/tasks.md new file mode 100644 index 0000000..02dc9c8 --- /dev/null +++ b/openspec/changes/archive/2026-01-20-add-loan-pricing-create/tasks.md @@ -0,0 +1,39 @@ +# Tasks: 添加利率定价流程创建功能 + +## Implementation Tasks + +### 1. 新增前端 API 接口 +- [x] 在 `ruoyi-ui/src/api/loanPricing/workflow.js` 中添加 `createWorkflow(data)` 函数 +- [x] 函数调用 `POST /loanPricing/workflow/create` 接口 + +### 2. 修改前端页面组件 +- [x] 在页面工具栏区域添加"新增"按钮 +- [x] 按钮配置权限控制 `v-hasPermi="['loanPricing:workflow:create']"` +- [x] 添加创建流程表单对话框组件 +- [x] 实现表单字段(参照 API 文档接口1的请求参数): + - 必填字段:机构编码(固定931000)、运行模式(固定1)、客户内码、客户类型、担保方式、申请金额、贷款利率 + - 可选字段:客户名称、证件类型、贷款用途 + - 业务标识字段:中间业务标识(个人快捷支付、个人电费代扣、企业电费代扣、企业水费代扣) + - 企业标识字段:净身企业、开立基本结算账户、制造业企业、省农担担保贷款、纳税信用等级A级、县级及以上农业龙头企业、普惠小微借款人 + - 抵质押信息字段:抵质押类型、抵质押物三方所有、是否有经营佐证 +- [x] 实现表单验证规则 +- [x] 实现 `handleAdd` 方法(打开对话框) +- [x] 实现 `submitForm` 方法(提交表单) +- [x] 实现 `cancelCreate` 方法(取消/关闭对话框) +- [x] 实现 `reset` 方法(重置表单) + +### 3. 配置数据库菜单权限 +- [x] 准备 SQL 插入语句,在 `sys_menu` 表中添加创建按钮权限: + - menu_id: 2003, menu_name: 流程创建, parent_id: 2001 + - perms: `loanPricing:workflow:create` +- [x] 执行 SQL 插入语句 +- [x] 关联管理员角色到新菜单权限 + +### 4. 验证和测试 +- [x] 代码实现完成,等待用户启动前端服务进行测试 +- [ ] 启动前端开发服务器 +- [ ] 验证"新增"按钮是否显示 +- [ ] 测试打开创建表单对话框 +- [ ] 测试表单验证功能 +- [ ] 测试提交创建成功后刷新列表 +- [ ] 测试取消和关闭对话框功能 diff --git a/openspec/changes/add-loan-pricing-frontend/proposal.md b/openspec/changes/archive/2026-01-20-add-loan-pricing-frontend/proposal.md similarity index 100% rename from openspec/changes/add-loan-pricing-frontend/proposal.md rename to openspec/changes/archive/2026-01-20-add-loan-pricing-frontend/proposal.md diff --git a/openspec/changes/add-loan-pricing-frontend/specs/loan-pricing-workflow-ui/spec.md b/openspec/changes/archive/2026-01-20-add-loan-pricing-frontend/specs/loan-pricing-workflow-ui/spec.md similarity index 100% rename from openspec/changes/add-loan-pricing-frontend/specs/loan-pricing-workflow-ui/spec.md rename to openspec/changes/archive/2026-01-20-add-loan-pricing-frontend/specs/loan-pricing-workflow-ui/spec.md diff --git a/openspec/changes/add-loan-pricing-frontend/tasks.md b/openspec/changes/archive/2026-01-20-add-loan-pricing-frontend/tasks.md similarity index 100% rename from openspec/changes/add-loan-pricing-frontend/tasks.md rename to openspec/changes/archive/2026-01-20-add-loan-pricing-frontend/tasks.md diff --git a/openspec/changes/add-loan-pricing-workflow/proposal.md b/openspec/changes/archive/2026-01-20-add-loan-pricing-workflow/proposal.md similarity index 100% rename from openspec/changes/add-loan-pricing-workflow/proposal.md rename to openspec/changes/archive/2026-01-20-add-loan-pricing-workflow/proposal.md diff --git a/openspec/changes/add-loan-pricing-workflow/specs/loan-pricing-workflow/spec.md b/openspec/changes/archive/2026-01-20-add-loan-pricing-workflow/specs/loan-pricing-workflow/spec.md similarity index 100% rename from openspec/changes/add-loan-pricing-workflow/specs/loan-pricing-workflow/spec.md rename to openspec/changes/archive/2026-01-20-add-loan-pricing-workflow/specs/loan-pricing-workflow/spec.md diff --git a/openspec/changes/add-loan-pricing-workflow/tasks.md b/openspec/changes/archive/2026-01-20-add-loan-pricing-workflow/tasks.md similarity index 100% rename from openspec/changes/add-loan-pricing-workflow/tasks.md rename to openspec/changes/archive/2026-01-20-add-loan-pricing-workflow/tasks.md diff --git a/openspec/specs/loan-pricing-workflow-ui/spec.md b/openspec/specs/loan-pricing-workflow-ui/spec.md new file mode 100644 index 0000000..c4bfa60 --- /dev/null +++ b/openspec/specs/loan-pricing-workflow-ui/spec.md @@ -0,0 +1,92 @@ +# loan-pricing-workflow-ui Specification + +## Purpose +TBD - created by archiving change add-loan-pricing-frontend. Update Purpose after archive. +## Requirements +### Requirement: 流程列表查询 + +系统 SHALL 提供利率定价流程列表查询页面,支持分页和多条件筛选,并支持创建新流程。 + +#### Scenario: 查询流程列表 +- **WHEN** 用户已登录系统且具有 `loanPricing:workflow:list` 权限,访问"利率定价管理 > 流程列表"菜单 +- **THEN** 系统显示利率定价流程列表页面,包含查询表单(客户名称模糊查询、创建者、机构号筛选)、搜索和重置按钮、新增按钮(带权限控制)、数据表格(业务方流水号、客户名称、客户类型、担保方式、申请金额、贷款利率、创建时间、创建者)、分页组件、操作列(包含"查看"按钮) + +#### Scenario: 使用筛选条件查询 +- **WHEN** 用户在流程列表页面输入客户名称或选择创建者/机构号,点击搜索按钮 +- **THEN** 系统根据筛选条件查询并更新列表数据 + +#### Scenario: 重置筛选条件 +- **WHEN** 用户已设置筛选条件,点击重置按钮 +- **THEN** 系统清空所有筛选条件并重新查询全部数据 + +### Requirement: 流程详情查看 + +系统 SHALL 提供流程详情查看功能,以对话框形式展示完整的流程信息。 + +#### Scenario: 查看流程详情 +- **WHEN** 用户在流程列表页面且具有 `loanPricing:workflow:query` 权限,点击列表中某条记录的"查看"按钮 +- **THEN** 系统弹出详情对话框,展示完整的流程信息(基本信息:业务方流水号、机构编码、客户内码、客户名称、证件类型;业务信息:客户类型、担保方式、申请金额、贷款利率、贷款用途;业务标识:中间业务标识、企业标识;抵质押信息:抵质押类型、是否三方所有;其他信息:创建时间、创建者、更新时间、更新者) + +### Requirement: 菜单和权限配置 + +系统 SHALL 在数据库中正确配置菜单项和权限,确保用户可以访问功能。 + +#### Scenario: 菜单显示和导航 +- **WHEN** 用户已登录系统且具有利率定价流程相关权限 +- **THEN** 系统在左侧菜单栏显示"利率定价管理"一级菜单,展开后显示"流程列表"二级菜单项 + +#### Scenario: 菜单路由配置 +- **WHEN** 用户点击"流程列表"菜单项,系统处理路由跳转 +- **THEN** 系统导航至 `/loanPricing/workflow` 路径,加载对应的前端组件 + +### Requirement: API 接口集成 + +前端 SHALL 正确调用后端 API 接口获取数据和提交创建请求。 + +#### Scenario: 列表接口调用 +- **WHEN** 用户访问流程列表页面,页面初始化或用户执行查询操作 +- **THEN** 前端调用 `GET /loanPricing/workflow/list` 接口,传入分页和筛选参数 + +#### Scenario: 详情接口调用 +- **WHEN** 用户点击查看按钮,前端获取选中记录的业务方流水号 +- **THEN** 前端调用 `GET /loanPricing/workflow/{serialNum}` 接口获取详情数据 + +#### Scenario: 创建接口调用 +- **WHEN** 用户填写完创建表单并点击确定按钮 +- **THEN** 前端调用 `POST /loanPricing/workflow/create` 接口,传入表单数据,成功后关闭对话框并刷新列表 + +### Requirement: 流程创建 + +系统 SHALL 提供创建新利率定价流程的功能,通过表单对话框收集必要信息。 + +#### Scenario: 打开创建表单 +- **WHEN** 用户在流程列表页面且具有 `loanPricing:workflow:create` 权限,点击"新增"按钮 +- **THEN** 系统弹出创建流程表单对话框,显示所有必填和可选字段 + +#### Scenario: 表单字段显示 +- **WHEN** 用户打开创建流程表单对话框 +- **THEN** 系统显示以下字段分组: + - 基本信息:客户内码(必填)、客户名称、客户类型(必填,下拉选择:个人/企业)、证件类型 + - 贷款信息:申请金额(必填)、贷款利率(必填)、担保方式(必填,下拉选择:信用/保证/抵押/质押)、贷款用途(下拉选择:consumer/business) + - 中间业务标识(个人):个人快捷支付(开关)、个人电费代扣(开关) + - 中间业务标识(企业):企业电费代扣(开关)、企业水费代扣(开关) + - 企业标识:净身企业(开关)、开立基本结算账户(开关)、制造业企业(开关)、省农担担保贷款(开关)、纳税信用等级A级(开关)、县级及以上农业龙头企业(开关)、普惠小微借款人(开关) + - 抵质押信息:抵质押类型(下拉选择:一线/一类/二类)、抵质押物三方所有(开关)、是否有经营佐证(开关) + - 固定字段:机构编码(隐藏,固定值931000)、运行模式(隐藏,固定值1) + +#### Scenario: 表单验证 +- **WHEN** 用户填写表单并点击确定按钮 +- **THEN** 系统验证必填字段:客户内码、客户类型、担保方式、申请金额、贷款利率,如有缺失则显示错误提示 + +#### Scenario: 提交创建成功 +- **WHEN** 用户填写完必填字段并点击确定按钮,后端返回成功响应 +- **THEN** 系统关闭对话框,显示成功提示消息,刷新列表数据 + +#### Scenario: 取消创建 +- **WHEN** 用户点击取消按钮或对话框关闭按钮 +- **THEN** 系统关闭对话框,不保存数据,不刷新列表 + +#### Scenario: 新增按钮权限控制 +- **WHEN** 用户不具有 `loanPricing:workflow:create` 权限 +- **THEN** 系统不显示"新增"按钮 + diff --git a/openspec/specs/loan-pricing-workflow/spec.md b/openspec/specs/loan-pricing-workflow/spec.md new file mode 100644 index 0000000..0cd008d --- /dev/null +++ b/openspec/specs/loan-pricing-workflow/spec.md @@ -0,0 +1,77 @@ +# loan-pricing-workflow Specification + +## Purpose +TBD - created by archiving change add-loan-pricing-workflow. Update Purpose after archive. +## Requirements +### Requirement: 利率定价流程发起 + +系统 SHALL 提供利率定价流程发起接口,允许业务人员创建新的利率定价申请。 + +#### Scenario: 成功发起利率定价流程 +- **WHEN** 业务人员提交包含必填字段(custIsn、custType、guarType、applyAmt、loanRate)的完整申请 +- **THEN** 系统自动生成业务方流水号(serialNum)并保存记录,返回成功响应 + +#### Scenario: 自动生成业务方流水号 +- **WHEN** 发起利率定价流程时 +- **THEN** 系统使用时间戳自动生成唯一的业务方流水号,无需用户输入 + +#### Scenario: 记录创建和更新信息 +- **WHEN** 利率定价流程创建成功 +- **THEN** 系统自动记录创建者、创建时间、更新者、更新时间 + +#### Scenario: 字段验证-必填字段 +- **WHEN** 提交的申请缺少必填字段(custIsn、custType、guarType、applyAmt、loanRate) +- **THEN** 系统返回参数验证失败的错误信息 + +#### Scenario: 字段验证-固定值字段 +- **WHEN** 提交的申请中 orgCode 非 "931000" 或 runType 非 "1" +- **THEN** 系统返回参数验证失败的错误信息 + +#### Scenario: 字段验证-枚举值 +- **WHEN** 提交的申请中 custType 不是"个人"或"企业" +- **THEN** 系统返回参数验证失败的错误信息 + +#### Scenario: 字段验证-担保方式 +- **WHEN** 提交的申请中 guarType 不是"信用"、"保证"、"抵押"、"质押"之一 +- **THEN** 系统返回参数验证失败的错误信息 + +### Requirement: 利率定价流程列表查询 + +系统 SHALL 提供利率定价流程列表查询接口,支持分页和多条件筛选。 + +#### Scenario: 默认按更新时间倒序排列 +- **WHEN** 业务人员查询利率定价流程列表 +- **THEN** 结果按更新时间(update_time)倒序排列 + +#### Scenario: 支持分页查询 +- **WHEN** 业务人员指定页码和每页数量查询列表 +- **THEN** 系统返回对应页的数据及总记录数 + +#### Scenario: 按创建者筛选 +- **WHEN** 业务人员按创建者筛选查询 +- **THEN** 系统返回该创建者创建的利率定价流程记录 + +#### Scenario: 按客户名称筛选 +- **WHEN** 业务人员按客户名称(custName)模糊查询 +- **THEN** 系统返回客户名称包含查询条件的记录 + +#### Scenario: 按机构号筛选 +- **WHEN** 业务人员按机构号(orgCode)筛选查询 +- **THEN** 系统返回该机构号的利率定价流程记录 + +#### Scenario: 组合条件筛选 +- **WHEN** 业务人员同时指定多个筛选条件 +- **THEN** 系统返回同时满足所有条件的记录 + +### Requirement: 利率定价流程详情查询 + +系统 SHALL 提供根据业务方流水号查询流程详情的接口。 + +#### Scenario: 根据业务方流水号查询详情 +- **WHEN** 业务人员提供有效的业务方流水号(serialNum) +- **THEN** 系统返回该流程的所有字段信息 + +#### Scenario: 查询不存在的流水号 +- **WHEN** 业务人员查询的业务方流水号不存在 +- **THEN** 系统返回"记录不存在"的错误信息 + diff --git a/ruoyi-ui/src/api/loanPricing/workflow.js b/ruoyi-ui/src/api/loanPricing/workflow.js index da348e4..f42dbb9 100644 --- a/ruoyi-ui/src/api/loanPricing/workflow.js +++ b/ruoyi-ui/src/api/loanPricing/workflow.js @@ -16,3 +16,12 @@ export function getWorkflow(serialNum) { method: 'get' }) } + +// 新增利率定价流程 +export function createWorkflow(data) { + return request({ + url: '/loanPricing/workflow/create', + method: 'post', + data: data + }) +} diff --git a/ruoyi-ui/src/views/loanPricing/workflow/index.vue b/ruoyi-ui/src/views/loanPricing/workflow/index.vue index 4ae71f1..870e799 100644 --- a/ruoyi-ui/src/views/loanPricing/workflow/index.vue +++ b/ruoyi-ui/src/views/loanPricing/workflow/index.vue @@ -32,6 +32,16 @@ + + 新增 + @@ -70,7 +80,7 @@ /> - + {{ detail.serialNum }} {{ detail.orgCode }} @@ -103,14 +113,190 @@ {{ detail.updateBy }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +