Files
crawler-plugin/admin-frontend-vue/src/api/user-secrets.ts
T
huangzd1997 5b8105ec2b feat(后台管理): 实体管理列表统一展示创建时间/更新时间
用户管理、菜单管理、不符合ASIN、数据去重总数据、查询ASIN、最低价ASIN、
商品类目、密钥管理共 8 个实体管理列表补齐两列。分组管理/店铺密钥/店铺管理
此前已带创建+修改时间,任务列表与统计报表(撞款监控、密钥用量、日志、
记录与版本)不含实体更新语义,均未改动。

关键点——时间列必须由数据库维护,否则新列是假的:
这些表的更新走 selectById → 改字段 → updateById,实体带着读出的旧
updated_at 一起写回。MySQL 规则是「UPDATE 显式给某列赋值时不触发该列的
ON UPDATE 自动更新」,不禁写就会把旧值写回去,更新时间永远冻结在首次写入
时刻。按 V125(biz_file_result)既有样板,给 7 个实体标注
@TableField(insertStrategy=NEVER, updateStrategy=NEVER)。

- V131:users / columns 补 updated_at(幂等 ADD COLUMN,仿 V125 写法)。
  存量行被回填为迁移执行时刻,非真实历史变更时间(历史上无记录,无法还原)
- 实体/VO:AdminUserEntity、PermissionMenuEntity、InvalidAsinDataEntity、
  DedupeTotalDataEntity、ProductCategoryEntity、QueryAsinEntity、
  SkipPriceAsinEntity 加/改写 updatedAt;AdminUserItemVo、PermissionMenuItemVo、
  InvalidAsinDataItemVo、DedupeTotalDataItemVo 补 updatedAt;
  AdminUserSecretRowVo 补 createdAt(行级首次配置时间 = 三模块最早)
- 查询ASIN/最低价ASIN 后端 VO 与前端 model 本就有两字段,仅补渲染
- 前端 8 页表格加列,同步修正空态/加载行的 colspan(手写表格,不同步会错位)
- 测试:align-query-asin / align-skip-price 原断言「不允许有更新时间列」
  (像素复刻旧版),按新需求改为断言两列存在;新增 e2e list-time-columns
  覆盖 8 页表头与真实时间值渲染
2026-09-19 15:53:01 +08:00

93 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { http } from './http'
import { unwrap } from './envelope'
/** 单模块状态(脱敏值 + 连通性)。 */
export interface AdminUserSecretModule {
moduleKey: string
moduleLabel: string
masked: string
/** 完整明文值:仅代理列有值(后端对密钥列不下发明文)。 */
full: string
exists: boolean
checkStatus: string
checkCode: string
checkMessage: string
checkLatencyMs: number | null
checkedAt: string | null
updatedAt: string | null
}
/** 一行一用户:三个字段列 + 行级状态(三类都检测通过才算 passed)。 */
export interface AdminUserSecretRow {
userId: number
username: string
/** 所属数据权限分组名(可能多个)。 */
groups: string[]
/** 所属主管(创建人)用户名;无分组时的兜底展示。 */
leaderUsername: string
similarAsin: AdminUserSecretModule
appearancePatent: AdminUserSecretModule
proxy: AdminUserSecretModule
status: string
statusMessage: string
/** 首次配置时间(三模块中最早);从未配置为 null。 */
createdAt: string | null
updatedAt: string | null
}
export interface AdminUserSecretPage {
items: AdminUserSecretRow[]
total: number
page: number
pageSize: number
/** 分组筛选项(超管=全部;主管=自己带的分组)。 */
groupOptions?: GroupOption[]
}
export interface GroupOption {
id: number
groupName: string
}
export interface UserSecretQuery {
keyword?: string
checkStatus?: string
/** 按数据权限分组筛选(仅超管生效);后端参数为 snake_case。 */
groupId?: number
page: number
pageSize: number
}
/** 分页查询用户密钥(一行一用户):GET /api/admin/user-secrets(筛选参数必须 snake_casecamel 会被后端静默忽略)。 */
export async function fetchUserSecretList(params: UserSecretQuery): Promise<AdminUserSecretPage> {
const query: Record<string, string | number> = { page: params.page, pageSize: params.pageSize }
if (params.keyword) query.keyword = params.keyword
if (params.checkStatus) query.checkStatus = params.checkStatus
if (params.groupId) query.group_id = params.groupId
const { data } = await http.get('/api/admin/user-secrets', { params: query })
return unwrap<AdminUserSecretPage>(data)
}
/** 立即检测该用户全部已配置项:POST /api/admin/user-secrets/{userId}/check
* 逐模块检测(每项最多「首查 + 传输层失败重试一次」,最坏约 31s/项),故放宽超时到 180s。 */
export async function checkUserSecret(userId: number) {
const { data } = await http.post(`/api/admin/user-secrets/${userId}/check`, undefined, { timeout: 180_000 })
return unwrap<
Array<{
moduleKey: string
checkStatus: string
checkCode: string
checkMessage: string
checkLatencyMs: number | null
checkedAt: string | null
viaProxy: boolean
}>
>(data)
}
/** 清空该用户全部密钥与代理配置:DELETE /api/admin/user-secrets/{userId} */
export async function deleteUserSecret(userId: number): Promise<void> {
const { data } = await http.delete(`/api/admin/user-secrets/${userId}`)
unwrap<unknown>(data)
}