修复气站用户配送点归属展示
This commit is contained in:
@@ -11,19 +11,30 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// gasUserListItem 在用户主档上附加当前气站服务关系中的配送点归属。
|
||||||
|
type gasUserListItem struct {
|
||||||
|
models.UserAccount
|
||||||
|
DeliveryBasicID uint64 `gorm:"column:delivery_basic_id" json:"delivery_basic_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// scopedUserList 将用户列表与当前气站唯一有效服务关系绑定,避免返回跨站归属。
|
||||||
|
func scopedUserList(databaseService *gorm.DB, gasBasicID uint64) *gorm.DB {
|
||||||
|
return common.ActiveRecords(databaseService.Model(&models.UserAccount{})).
|
||||||
|
Select("user_account.*, user_service_relation.delivery_basic_id AS delivery_basic_id").
|
||||||
|
Joins("JOIN user_service_relation ON user_service_relation.user_account_id = user_account.id AND user_service_relation.status <> ?", common.StatusArchived).
|
||||||
|
Where("user_service_relation.gas_basic_id = ?", gasBasicID)
|
||||||
|
}
|
||||||
|
|
||||||
func ListUser(ctx *gin.Context) {
|
func ListUser(ctx *gin.Context) {
|
||||||
station, ok := currentGas(ctx)
|
station, ok := currentGas(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
page, size := common.PageSize(ctx)
|
page, size := common.PageSize(ctx)
|
||||||
var list []models.UserAccount
|
var list []gasUserListItem
|
||||||
var total int64
|
var total int64
|
||||||
query := common.ApplyKeywordFilter(ctx,
|
query := common.ApplyKeywordFilter(ctx,
|
||||||
common.ActiveRecords(impl.DBService.Model(&models.UserAccount{})).
|
scopedUserList(impl.DBService, station.ID),
|
||||||
Joins("JOIN user_service_relation ON user_service_relation.user_account_id = user_account.id AND user_service_relation.status <> ?",
|
|
||||||
common.StatusArchived).
|
|
||||||
Where("user_service_relation.gas_basic_id = ?", station.ID),
|
|
||||||
&models.UserAccount{})
|
&models.UserAccount{})
|
||||||
if err := query.Count(&total).Error; err != nil {
|
if err := query.Count(&total).Error; err != nil {
|
||||||
infra.Response.Error(ctx, err)
|
infra.Response.Error(ctx, err)
|
||||||
|
|||||||
32
backend/api/internal/logic/gas/user_test.go
Normal file
32
backend/api/internal/logic/gas/user_test.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package gas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestScopedUserListIncludesDeliveryRelation 验证用户列表带出当前气站服务关系的配送点主键。
|
||||||
|
func TestScopedUserListIncludesDeliveryRelation(t *testing.T) {
|
||||||
|
connection, _, err := sqlmock.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create SQL mock: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { _ = connection.Close() })
|
||||||
|
databaseService, err := gorm.Open(postgres.New(postgres.Config{Conn: connection}), &gorm.Config{DryRun: true})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("open GORM database: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
statement := scopedUserList(databaseService, 42).
|
||||||
|
Find(&[]gasUserListItem{}).Statement.SQL.String()
|
||||||
|
if !strings.Contains(statement, "user_service_relation.delivery_basic_id AS delivery_basic_id") {
|
||||||
|
t.Fatalf("user list must select delivery relation, got: %s", statement)
|
||||||
|
}
|
||||||
|
if !strings.Contains(statement, "user_service_relation.gas_basic_id =") {
|
||||||
|
t.Fatalf("user list must keep gas scope, got: %s", statement)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
- 复用 5173 的账户摘要组件和头像组合式逻辑,在新建、详情、编辑页显示头像选择与预览。
|
- 复用 5173 的账户摘要组件和头像组合式逻辑,在新建、详情、编辑页显示头像选择与预览。
|
||||||
- 启用工作人员与用户列表的受保护头像缩略图,替代头像存储路径文本。
|
- 启用工作人员与用户列表的受保护头像缩略图,替代头像存储路径文本。
|
||||||
|
- 用户账户列表从当前气站服务关系带出配送点归属,修复配送点唯一标识显示为空。
|
||||||
- 从基础信息表单移除头像文本输入框,保存时仅在选择新头像或恢复默认头像后提交头像字段。
|
- 从基础信息表单移除头像文本输入框,保存时仅在选择新头像或恢复默认头像后提交头像字段。
|
||||||
- 新增工作人员、用户头像读取路由,查询始终附加当前气站或服务关系范围。
|
- 新增工作人员、用户头像读取路由,查询始终附加当前气站或服务关系范围。
|
||||||
- 将更新请求的头像字段改为可选指针,未修改头像时不更新数据库列。
|
- 将更新请求的头像字段改为可选指针,未修改头像时不更新数据库列。
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
|
|
||||||
- `/staff/add` 顶部显示账户摘要与头像选择区,布局和交互参考 5173。
|
- `/staff/add` 顶部显示账户摘要与头像选择区,布局和交互参考 5173。
|
||||||
- 工作人员与用户列表显示 32 像素圆形头像;无头像或加载失败时回退默认头像。
|
- 工作人员与用户列表显示 32 像素圆形头像;无头像或加载失败时回退默认头像。
|
||||||
|
- 用户账户列表以“所属配送点”为列名并显示配送点业务名称,唯一标识仅用于悬停查看和复制。
|
||||||
- 工作人员与用户的详情、编辑页可安全读取现有头像。
|
- 工作人员与用户的详情、编辑页可安全读取现有头像。
|
||||||
- 普通资料编辑不会误清空头像;主动恢复默认头像仍会提交空值。
|
- 普通资料编辑不会误清空头像;主动恢复默认头像仍会提交空值。
|
||||||
|
|
||||||
@@ -30,7 +32,10 @@
|
|||||||
- `frontend/gas_admin/src/views/resource/ResourceRecordPage.vue`:启用头像摘要、过滤文本字段、接入上传载荷与未保存检测。
|
- `frontend/gas_admin/src/views/resource/ResourceRecordPage.vue`:启用头像摘要、过滤文本字段、接入上传载荷与未保存检测。
|
||||||
- `frontend/gas_admin/src/api/avatar.ts`:使用气站 API 前缀读取受保护头像。
|
- `frontend/gas_admin/src/api/avatar.ts`:使用气站 API 前缀读取受保护头像。
|
||||||
- `frontend/gas_admin/src/views/shared/protected-list-avatar-loader.ts`:开放工作人员与用户缩略图渲染。
|
- `frontend/gas_admin/src/views/shared/protected-list-avatar-loader.ts`:开放工作人员与用户缩略图渲染。
|
||||||
|
- `frontend/gas_admin/src/api/resources.ts`:用户账户的配送点关系列表以业务名称为主展示,并保留唯一标识复制能力。
|
||||||
|
- `frontend/gas_admin/scripts/check-resource-pages.mjs`:新增用户账户配送点名称展示的静态回归检查。
|
||||||
- `backend/api/internal/logic/gas/staff.go`、`user.go`:新增范围受限头像读取并保护头像更新语义。
|
- `backend/api/internal/logic/gas/staff.go`、`user.go`:新增范围受限头像读取并保护头像更新语义。
|
||||||
|
- `backend/api/internal/logic/gas/user_test.go`:校验用户列表携带配送点关系且保留气站范围。
|
||||||
- `backend/api/internal/routers/gas_business.go`、`gas_test.go`:注册并校验头像路由。
|
- `backend/api/internal/routers/gas_business.go`、`gas_test.go`:注册并校验头像路由。
|
||||||
|
|
||||||
## 验证结果
|
## 验证结果
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* 功能:静态检查气站管理端全部标准资源是否具备独立记录页配置。
|
* 功能:静态检查气站管理端全部标准资源是否具备独立记录页配置。
|
||||||
* 版本:v1.2.0
|
* 版本:v1.3.0
|
||||||
*/
|
*/
|
||||||
import { readFileSync, existsSync } from 'node:fs';
|
import { readFileSync, existsSync } from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
@@ -83,6 +83,13 @@ for (const marker of [
|
|||||||
if (!listSource.includes('recordEditReason(record)')) {
|
if (!listSource.includes('recordEditReason(record)')) {
|
||||||
fail('合同列表缺少编辑禁用原因提示');
|
fail('合同列表缺少编辑禁用原因提示');
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
!resourceSource.includes(
|
||||||
|
"relation('delivery_basic_identity', '/delivery_basic', true, { label: '所属配送点', listLabel: '所属配送点', listRelationNameOnly: true, showIdentityCopy: true })",
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
fail('用户账户列表未按文档以所属配送点名称为主展示');
|
||||||
|
}
|
||||||
|
|
||||||
const createCount = listResources.filter((item) => createModes.has(item.mode)).length;
|
const createCount = listResources.filter((item) => createModes.has(item.mode)).length;
|
||||||
console.log(
|
console.log(
|
||||||
|
|||||||
@@ -513,7 +513,7 @@ const gasOverrides: ResourceUiDefinition[] = [
|
|||||||
{ name: '重置密码', resource: '/staff_account/:identity/password', method: 'PUT', fields: [f('password', { required: true })] },
|
{ name: '重置密码', resource: '/staff_account/:identity/password', method: 'PUT', fields: [f('password', { required: true })] },
|
||||||
]),
|
]),
|
||||||
define('staff_credential', '工作人员资质', 'writable', [relation('staff_account_identity', '/staff_account', true, { staffRelation: { roles: 'context', lockPrefilled: true, showIdentityCopy: true } }), f('credential_type', { required: true }), f('credential_no'), f('expired_at')]),
|
define('staff_credential', '工作人员资质', 'writable', [relation('staff_account_identity', '/staff_account', true, { staffRelation: { roles: 'context', lockPrefilled: true, showIdentityCopy: true } }), f('credential_type', { required: true }), f('credential_no'), f('expired_at')]),
|
||||||
define('user_account', '用户账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('real_name'), relation('delivery_basic_identity', '/delivery_basic', true)], 'list', [
|
define('user_account', '用户账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('real_name'), relation('delivery_basic_identity', '/delivery_basic', true, { label: '所属配送点', listLabel: '所属配送点', listRelationNameOnly: true, showIdentityCopy: true })], 'list', [
|
||||||
{ name: '重置密码', resource: '/user_account/:identity/password', method: 'PUT', fields: [f('password', { required: true })] },
|
{ name: '重置密码', resource: '/user_account/:identity/password', method: 'PUT', fields: [f('password', { required: true })] },
|
||||||
]),
|
]),
|
||||||
define('user_address', '用户地址', 'writable', [relation('user_account_identity', '/user_account', true), f('address', { required: true }), f('longitude'), f('latitude'), f('is_default')]),
|
define('user_address', '用户地址', 'writable', [relation('user_account_identity', '/user_account', true), f('address', { required: true }), f('longitude'), f('latitude'), f('is_default')]),
|
||||||
|
|||||||
Reference in New Issue
Block a user