完善合同关联主体名称展示
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// 功能描述:为配送合同提供签约用户名称和当前服务关系状态,不新增数据库字段。
|
// 功能描述:为配送合同提供关联主体名称和当前服务关系状态,不新增数据库字段。
|
||||||
// 版本:v1.0.0
|
// 版本:v1.1.0
|
||||||
package gasorder
|
package gasorder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -8,18 +8,25 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContractPartyDisplay 在合同主档上附加可读用户名称和当前气站服务关系状态。
|
// ContractPartyDisplay 在合同主档上附加可读关联名称和当前气站服务关系状态。
|
||||||
type ContractPartyDisplay struct {
|
type ContractPartyDisplay struct {
|
||||||
models.GasorderContract
|
models.GasorderContract
|
||||||
UserAccountDisplayName string `gorm:"column:user_account_display_name" json:"user_account_display_name"`
|
UserAccountDisplayName string `gorm:"column:user_account_display_name" json:"user_account_display_name"`
|
||||||
|
GasBasicDisplayName string `gorm:"column:gas_basic_display_name" json:"gas_basic_display_name"`
|
||||||
|
DeliveryDisplayName string `gorm:"column:delivery_basic_display_name" json:"delivery_basic_display_name"`
|
||||||
UserServiceActive bool `gorm:"column:user_service_active" json:"user_service_active"`
|
UserServiceActive bool `gorm:"column:user_service_active" json:"user_service_active"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractPartyDisplayQuery 仅通过合同已保存的用户主键读取名称,不开放平台用户全集。
|
// ContractPartyDisplayQuery 仅通过合同已保存的关联主键读取名称,不开放平台主体全集。
|
||||||
func ContractPartyDisplayQuery(databaseService *gorm.DB) *gorm.DB {
|
func ContractPartyDisplayQuery(databaseService *gorm.DB) *gorm.DB {
|
||||||
return common.ActiveRecords(databaseService.Model(&models.GasorderContract{})).
|
return common.ActiveRecords(databaseService.Model(&models.GasorderContract{})).
|
||||||
Select(`gasorder_contract.*,
|
Select(`gasorder_contract.*,
|
||||||
COALESCE(NULLIF(contract_user.name, ''), NULLIF(contract_user.real_name, ''), NULLIF(contract_user.username, ''), '签约用户记录已失效') AS user_account_display_name,
|
COALESCE(NULLIF(contract_user.name, ''), NULLIF(contract_user.real_name, ''), NULLIF(contract_user.username, ''), '签约用户记录已失效') AS user_account_display_name,
|
||||||
|
COALESCE(NULLIF(contract_gas.name, ''), '签约气站记录已失效') AS gas_basic_display_name,
|
||||||
|
CASE
|
||||||
|
WHEN gasorder_contract.delivery_basic_id = 0 THEN '未指定配送点'
|
||||||
|
ELSE COALESCE(NULLIF(contract_delivery.name, ''), '配送点记录已失效')
|
||||||
|
END AS delivery_basic_display_name,
|
||||||
EXISTS (
|
EXISTS (
|
||||||
SELECT 1 FROM user_service_relation AS contract_relation
|
SELECT 1 FROM user_service_relation AS contract_relation
|
||||||
WHERE contract_relation.user_account_id = gasorder_contract.user_account_id
|
WHERE contract_relation.user_account_id = gasorder_contract.user_account_id
|
||||||
@@ -27,7 +34,9 @@ func ContractPartyDisplayQuery(databaseService *gorm.DB) *gorm.DB {
|
|||||||
AND contract_relation.status <> ?
|
AND contract_relation.status <> ?
|
||||||
AND contract_relation.deleted_at IS NULL
|
AND contract_relation.deleted_at IS NULL
|
||||||
) AS user_service_active`, common.StatusArchived).
|
) AS user_service_active`, common.StatusArchived).
|
||||||
Joins("LEFT JOIN user_account AS contract_user ON contract_user.id = gasorder_contract.user_account_id")
|
Joins("LEFT JOIN user_account AS contract_user ON contract_user.id = gasorder_contract.user_account_id").
|
||||||
|
Joins("LEFT JOIN gas_basic AS contract_gas ON contract_gas.id = gasorder_contract.gas_basic_id").
|
||||||
|
Joins("LEFT JOIN delivery_basic AS contract_delivery ON contract_delivery.id = gasorder_contract.delivery_basic_id")
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractUserServiceActive 校验签约用户当前仍由合同气站服务。
|
// contractUserServiceActive 校验签约用户当前仍由合同气站服务。
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func TestFilterGasorderContractCandidates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestContractPartyDisplayQuery 验证合同名称直接按合同用户主键读取,并计算同气站服务关系状态。
|
// TestContractPartyDisplayQuery 验证合同关联名称按已保存主键读取,并计算同气站服务关系状态。
|
||||||
func TestContractPartyDisplayQuery(t *testing.T) {
|
func TestContractPartyDisplayQuery(t *testing.T) {
|
||||||
sqlDatabase, _, err := sqlmock.New()
|
sqlDatabase, _, err := sqlmock.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -93,7 +93,11 @@ func TestContractPartyDisplayQuery(t *testing.T) {
|
|||||||
})
|
})
|
||||||
for _, fragment := range []string{
|
for _, fragment := range []string{
|
||||||
"LEFT JOIN user_account AS contract_user",
|
"LEFT JOIN user_account AS contract_user",
|
||||||
|
"LEFT JOIN gas_basic AS contract_gas",
|
||||||
|
"LEFT JOIN delivery_basic AS contract_delivery",
|
||||||
"user_account_display_name",
|
"user_account_display_name",
|
||||||
|
"gas_basic_display_name",
|
||||||
|
"delivery_basic_display_name",
|
||||||
"contract_relation.user_account_id = gasorder_contract.user_account_id",
|
"contract_relation.user_account_id = gasorder_contract.user_account_id",
|
||||||
"contract_relation.gas_basic_id = gasorder_contract.gas_basic_id",
|
"contract_relation.gas_basic_id = gasorder_contract.gas_basic_id",
|
||||||
"gasorder_contract.gas_basic_id = 42",
|
"gasorder_contract.gas_basic_id = 42",
|
||||||
|
|||||||
@@ -17,12 +17,16 @@
|
|||||||
5. 为 JSON 参数增加“查看参数”弹层,支持对象、合法 JSON 字符串和历史普通文本。
|
5. 为 JSON 参数增加“查看参数”弹层,支持对象、合法 JSON 字符串和历史普通文本。
|
||||||
6. 明确当前设备名称与合同绑定快照的语义区别。
|
6. 明确当前设备名称与合同绑定快照的语义区别。
|
||||||
7. 当签约用户不再属于合同气站时,仅保留终止合同操作。
|
7. 当签约用户不再属于合同气站时,仅保留终止合同操作。
|
||||||
|
8. 根据修复后页面截图收尾:合同关系字段使用“所属气站、所属配送点”,状态标签改为内容宽度,避免在详情网格中拉伸成整条色块。
|
||||||
|
9. 补齐合同详情接口的气站、配送点只读名称字段,使三个主体关系统一显示“名称 + 唯一标识复制”,不再把 UUID 当作主要文案。
|
||||||
|
|
||||||
## 行为变化
|
## 行为变化
|
||||||
|
|
||||||
- 变更前:未知英文键直接成为标签,子表列序随响应漂移。
|
- 变更前:未知英文键直接成为标签,子表列序随响应漂移。
|
||||||
- 变更后:只显示具有中文业务契约的字段;合同气瓶和修订记录使用固定列与完整参数查看器。
|
- 变更后:只显示具有中文业务契约的字段;合同气瓶和修订记录使用固定列与完整参数查看器。
|
||||||
- 兼容性:不修改 API、数据库和原始业务值,未知历史状态或动作仍保留原编码用于排障。
|
- 兼容性:不修改 API 原字段、数据库和原始业务值,仅新增只读名称;未知历史状态或动作仍保留原编码用于排障。
|
||||||
|
- 视觉变化:状态标签保留状态颜色但紧凑展示;合同关联对象使用业务称呼,不再显示“气站唯一标识、配送点唯一标识”等技术标签。
|
||||||
|
- 接口变化:合同详情兼容性新增 `gas_basic_display_name`、`delivery_basic_display_name`;原 identity 字段、数据库结构和写入协议不变。
|
||||||
|
|
||||||
## 验证结果
|
## 验证结果
|
||||||
|
|
||||||
@@ -30,7 +34,9 @@
|
|||||||
- `npm.cmd run resource-pages:check`:通过,详情 17、新建 9、编辑 5,并覆盖详情白名单、固定列序、关系合并、JSON 查看器和动作限制。
|
- `npm.cmd run resource-pages:check`:通过,详情 17、新建 9、编辑 5,并覆盖详情白名单、固定列序、关系合并、JSON 查看器和动作限制。
|
||||||
- `npm.cmd run contract:check`、`npm.cmd run profile:check`:通过。
|
- `npm.cmd run contract:check`、`npm.cmd run profile:check`:通过。
|
||||||
- `npm.cmd run build`:通过,2643 个模块完成生产构建。
|
- `npm.cmd run build`:通过,2643 个模块完成生产构建。
|
||||||
- 应用内浏览器当前没有已登录的 5176 标签页,自动视觉回归被登录状态阻挡;用户提供的修复前截图已用于定位,不替代修复后回归。
|
- `go test ./api/internal/logic/platform/gasorder ./api/internal/logic/delivery`:通过。
|
||||||
|
- 用户提供的修复后截图确认英文辅助键、固定中文列序与 JSON 查看入口已生效,并据此发现和修正状态标签拉伸、关系标签技术化问题。
|
||||||
|
- 后端已重新构建并恢复监听 12426;旧程序保存在 `platform-api.before-contract-relation-display.exe`,可回退。
|
||||||
|
|
||||||
## 风险评估
|
## 风险评估
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
本项目将 5176 配送点管理端的标准资源详情展示对齐 5173。所有管理员可见字段必须来自显式中文契约;后端辅助键、内部 ID 和未知响应字段不得直接成为页面标签。
|
本项目将 5176 配送点管理端的标准资源详情展示对齐 5173。所有管理员可见字段必须来自显式中文契约;后端辅助键、内部 ID 和未知响应字段不得直接成为页面标签。
|
||||||
|
|
||||||
技术栈:Vue 3、TypeScript、Arco Design。功能仅修改展示层和前端动作可见性,不修改数据库及聚合详情接口。
|
技术栈:Vue 3、TypeScript、Arco Design、Go/GORM。功能不修改数据库;聚合详情接口仅向下兼容新增关联主体只读名称。
|
||||||
|
|
||||||
## 2. 目录结构
|
## 2. 目录结构
|
||||||
|
|
||||||
@@ -14,6 +14,8 @@ frontend/delivery_admin/src/
|
|||||||
├── api/resource-display.ts # 中文标签、状态、动作、金额与时间格式化
|
├── api/resource-display.ts # 中文标签、状态、动作、金额与时间格式化
|
||||||
├── views/resource/ResourceDetailContent.vue # 白名单详情与关联表渲染
|
├── views/resource/ResourceDetailContent.vue # 白名单详情与关联表渲染
|
||||||
└── views/resource/ResourceRecordPage.vue # 服务关系失效时的动作限制
|
└── views/resource/ResourceRecordPage.vue # 服务关系失效时的动作限制
|
||||||
|
backend/api/internal/logic/platform/gasorder/
|
||||||
|
└── contract_party_display.go # 合同关联主体只读名称和服务关系状态
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. 核心规则
|
## 3. 核心规则
|
||||||
@@ -25,6 +27,8 @@ frontend/delivery_admin/src/
|
|||||||
- `user_service_active` 显示为“签约用户仍属合同气站:是/否”;为否时只保留终止合同操作。
|
- `user_service_active` 显示为“签约用户仍属合同气站:是/否”;为否时只保留终止合同操作。
|
||||||
- 合同气瓶和修订记录使用固定中文列序,超宽表格横向滚动。
|
- 合同气瓶和修订记录使用固定中文列序,超宽表格横向滚动。
|
||||||
- 名称字段与唯一标识组合展示;`product_params` 通过弹层完整查看格式化 JSON。
|
- 名称字段与唯一标识组合展示;`product_params` 通过弹层完整查看格式化 JSON。
|
||||||
|
- 合同主记录中的气站、配送点关系分别标为“所属气站、所属配送点”;状态标签按内容宽度紧凑展示。
|
||||||
|
- 用户、气站、配送点关系均以服务端返回的当前名称为主文案,并保留唯一标识复制;名称失效时显示明确的失效占位,不把 UUID 退化成主要名称。
|
||||||
|
|
||||||
## 4. 合同气瓶语义
|
## 4. 合同气瓶语义
|
||||||
|
|
||||||
@@ -36,4 +40,4 @@ frontend/delivery_admin/src/
|
|||||||
|
|
||||||
## 6. 变更记录
|
## 6. 变更记录
|
||||||
|
|
||||||
- v1.0(2026-08-23):新增配送端详情中文白名单、合同固定子表、JSON 查看器、关系合并和合同动作限制。
|
- v1.0(2026-08-23):新增配送端详情中文白名单、合同固定子表、JSON 查看器、关系合并和合同动作限制,并完成关系标签与紧凑状态标签的视觉收尾。
|
||||||
|
|||||||
@@ -98,6 +98,15 @@ const collectionAliases: Record<string, Record<string, string>> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 主记录关系字段按资源覆盖全局技术名称,避免把内部关联键暴露为页面文案。 */
|
||||||
|
const resourceAliases: Record<string, Record<string, string>> = {
|
||||||
|
gasorder_contract: {
|
||||||
|
user_account_identity: '用户账户',
|
||||||
|
gas_basic_identity: '所属气站',
|
||||||
|
delivery_basic_identity: '所属配送点',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const moneyKeys = new Set([
|
const moneyKeys = new Set([
|
||||||
'amount',
|
'amount',
|
||||||
'unit_price',
|
'unit_price',
|
||||||
@@ -128,6 +137,7 @@ export function detailFieldLabel(
|
|||||||
const normalized = key.endsWith('_masked') ? key.slice(0, -7) : key;
|
const normalized = key.endsWith('_masked') ? key.slice(0, -7) : key;
|
||||||
return (
|
return (
|
||||||
collectionAliases[`${definition.name}.${collectionKey}`]?.[normalized] ??
|
collectionAliases[`${definition.name}.${collectionKey}`]?.[normalized] ??
|
||||||
|
resourceAliases[definition.name]?.[normalized] ??
|
||||||
definition.fields.find((field) => field.key === normalized)?.label ??
|
definition.fields.find((field) => field.key === normalized)?.label ??
|
||||||
aliases[normalized] ??
|
aliases[normalized] ??
|
||||||
defaultResourceFieldLabel(normalized)
|
defaultResourceFieldLabel(normalized)
|
||||||
|
|||||||
@@ -16,10 +16,12 @@
|
|||||||
</a-popover>
|
</a-popover>
|
||||||
<a-tag
|
<a-tag
|
||||||
v-else-if="entry.key === 'contract_status'"
|
v-else-if="entry.key === 'contract_status'"
|
||||||
|
class="detail-status-tag"
|
||||||
:color="contractStatusColor(Number(entry.rawValue))"
|
:color="contractStatusColor(Number(entry.rawValue))"
|
||||||
>{{ entry.value }}</a-tag>
|
>{{ entry.value }}</a-tag>
|
||||||
<a-tag
|
<a-tag
|
||||||
v-else-if="entry.key === 'user_service_active'"
|
v-else-if="entry.key === 'user_service_active'"
|
||||||
|
class="detail-status-tag"
|
||||||
:color="entry.rawValue === true ? 'green' : 'red'"
|
:color="entry.rawValue === true ? 'green' : 'red'"
|
||||||
>{{ entry.value }}</a-tag>
|
>{{ entry.value }}</a-tag>
|
||||||
<IdentityText
|
<IdentityText
|
||||||
@@ -99,6 +101,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import {
|
||||||
|
collectionRelationIdentityKey,
|
||||||
|
isCollectionJsonField,
|
||||||
|
isResourceJsonField,
|
||||||
|
resourceDetailContract,
|
||||||
|
} from '@/api/resource-detail-contract';
|
||||||
import {
|
import {
|
||||||
contractStatusColor,
|
contractStatusColor,
|
||||||
detailFieldLabel,
|
detailFieldLabel,
|
||||||
@@ -107,12 +115,6 @@ import {
|
|||||||
isEmptyDeletedAt,
|
isEmptyDeletedAt,
|
||||||
primaryRecord,
|
primaryRecord,
|
||||||
} from '@/api/resource-display';
|
} from '@/api/resource-display';
|
||||||
import {
|
|
||||||
collectionRelationIdentityKey,
|
|
||||||
isCollectionJsonField,
|
|
||||||
isResourceJsonField,
|
|
||||||
resourceDetailContract,
|
|
||||||
} from '@/api/resource-detail-contract';
|
|
||||||
import type { ResourceRow } from '@/api/resource-record-form';
|
import type { ResourceRow } from '@/api/resource-record-form';
|
||||||
import type { ResourceUiDefinition } from '@/api/resources';
|
import type { ResourceUiDefinition } from '@/api/resources';
|
||||||
import IdentityText from '@/components/IdentityText.vue';
|
import IdentityText from '@/components/IdentityText.vue';
|
||||||
@@ -308,6 +310,7 @@ const collections = computed(() =>
|
|||||||
}
|
}
|
||||||
.detail-item-wide { grid-column: 1 / -1; }
|
.detail-item-wide { grid-column: 1 / -1; }
|
||||||
.detail-label { color: var(--color-text-3); white-space: nowrap; }
|
.detail-label { color: var(--color-text-3); white-space: nowrap; }
|
||||||
|
.detail-status-tag { width: fit-content; justify-self: start; }
|
||||||
.detail-value { min-width: 0; color: var(--color-text-1); white-space: pre-wrap; word-break: break-word; }
|
.detail-value { min-width: 0; color: var(--color-text-1); white-space: pre-wrap; word-break: break-word; }
|
||||||
.json-value,
|
.json-value,
|
||||||
.collection-json-value {
|
.collection-json-value {
|
||||||
|
|||||||
Reference in New Issue
Block a user