diff --git a/apps/admin-web/src/lib/api.ts b/apps/admin-web/src/lib/api.ts index 386e1b1..fd37d14 100644 --- a/apps/admin-web/src/lib/api.ts +++ b/apps/admin-web/src/lib/api.ts @@ -169,6 +169,7 @@ export type AdminUserRow = { wxOpenId: string | null; wechatVerified: boolean; nickname: string | null; + hqRemark: string | null; status: number; sourceType: string; sourceRefId: string | null; diff --git a/apps/admin-web/src/pages/UsersPage.tsx b/apps/admin-web/src/pages/UsersPage.tsx index fd8c8c0..9678042 100644 --- a/apps/admin-web/src/pages/UsersPage.tsx +++ b/apps/admin-web/src/pages/UsersPage.tsx @@ -31,40 +31,40 @@ type UserOrderRow = { createdAt: string; }; -function NicknameCell({ +function HqRemarkCell({ id, - nickname, + hqRemark, onSaved, }: { id: string; - nickname: string | null; - onSaved: (id: string, nickname: string | null) => void; + hqRemark: string | null; + onSaved: (id: string, hqRemark: string | null) => void; }) { const [editing, setEditing] = useState(false); - const [value, setValue] = useState(nickname ?? ''); + const [value, setValue] = useState(hqRemark ?? ''); const savingRef = useRef(false); useEffect(() => { - if (!editing) setValue(nickname ?? ''); - }, [nickname, editing]); + if (!editing) setValue(hqRemark ?? ''); + }, [hqRemark, editing]); async function commit() { if (savingRef.current) return; const next = value.trim() || null; - const prev = nickname?.trim() || null; + const prev = hqRemark?.trim() || null; setEditing(false); if (next === prev) return; savingRef.current = true; try { - const res = await request<{ id: string; nickname: string | null }>(`/admin/users/${id}`, { + const res = await request<{ id: string; hqRemark: string | null }>(`/admin/users/${id}`, { method: 'PUT', - body: JSON.stringify({ nickname: next ?? '' }), + body: JSON.stringify({ hqRemark: next ?? '' }), }); - onSaved(id, res.nickname); - message.success('昵称已保存'); + onSaved(id, res.hqRemark); + message.success('备注已保存'); } catch (e) { message.error(e instanceof Error ? e.message : '保存失败'); - setValue(nickname ?? ''); + setValue(hqRemark ?? ''); } finally { savingRef.current = false; } @@ -75,7 +75,7 @@ function NicknameCell({ setValue(e.target.value)} onBlur={() => void commit()} @@ -90,14 +90,14 @@ function NicknameCell({ return ( { e.stopPropagation(); setEditing(true); }} style={{ cursor: 'text', display: 'inline-block', minWidth: 48 }} > - {nickname || '—'} + {hqRemark || '—'} ); } @@ -332,17 +332,23 @@ export default function UsersPage() { title: '昵称', dataIndex: 'nickname', width: 140, + render: (v: string | null) => v || '—', + }, + { + title: '备注', + dataIndex: 'hqRemark', + width: 160, render: (v: string | null, row) => ( - { + hqRemark={v} + onSaved={(id, hqRemark) => { setData((prev) => prev - ? { ...prev, items: prev.items.map((u) => (u.id === id ? { ...u, nickname } : u)) } + ? { ...prev, items: prev.items.map((u) => (u.id === id ? { ...u, hqRemark } : u)) } : prev, ); - setDetail((d) => (d && d.id === id ? { ...d, nickname } : d)); + setDetail((d) => (d && d.id === id ? { ...d, hqRemark } : d)); }} /> ), @@ -558,6 +564,7 @@ export default function UsersPage() { {detail.id} {detail.userNo} {detail.nickname || '—'} + {detail.hqRemark || '—'} {maskPhone(detail.phone)} {detail.phoneVerifiedAt ? new Date(detail.phoneVerifiedAt).toLocaleString('zh-CN') : '未验证'} diff --git a/docs/杜康好客-v3-PRD.md b/docs/杜康好客-v3-PRD.md index 20ce8eb..a411468 100644 --- a/docs/杜康好客-v3-PRD.md +++ b/docs/杜康好客-v3-PRD.md @@ -28,7 +28,7 @@ | 3.5.6 | 08-23 | HQ 任务导出;核销/财务银行信息;工单图片入任务;运营去调试;门店审核全屏 | [`v3.5.6`](./杜康好客-v3.5.6-开发文档.md) | | 3.5.7 | 08-23 | 门店端核销记录时间显示秒;首页「今日核销金额」文案 | [`v3.5.7`](./杜康好客-v3.5.7-开发文档.md) | | 3.5.8 | 08-24 | HQ 单账号追加/撤销权限;运营改客服修复;城市门店服务+城市范围 | [`v3.5.8`](./杜康好客-v3.5.8-开发文档.md) | -| 3.5.9 | 08-25 | 门店累计核销好客权益;HQ 表格去省略号;序号列 + 列设置;用户列表改昵称 / 手机号不脱敏 | [`v3.5.9`](./杜康好客-v3.5.9-开发文档.md) | +| 3.5.9 | 08-25 | 门店累计核销好客权益;HQ 表格去省略号;序号列 + 列设置;用户备注 / 手机号不脱敏 | [`v3.5.9`](./杜康好客-v3.5.9-开发文档.md) | --- @@ -139,7 +139,7 @@ - HQ 门店列表:店名完整展示、操作列右固定、表过宽可横向滚动(v3.5.9 去掉省略号)。 - HQ 主列表:最左序号;「列设置」控制显示列与顺序,偏好挂当前 HQ 账号。 - 门店列表展示「累计核销好客权益」(该店核销券面额合计)。 -- HQ 用户列表:双击昵称、离开编辑即保存;列表手机号不脱敏(详情仍脱敏)。 +- HQ 用户列表:昵称只读;双击「备注」离开即保存(`hq_remark`);列表手机号不脱敏(详情仍脱敏)。 - 详 [`v3.4.16`](./杜康好客-v3.4.16-门店联系电话与体验优化.md)、[`v3.5.9`](./杜康好客-v3.5.9-开发文档.md)。 --- diff --git a/docs/杜康好客-v3-现状对照.md b/docs/杜康好客-v3-现状对照.md index 52a41c9..d1133bb 100644 --- a/docs/杜康好客-v3-现状对照.md +++ b/docs/杜康好客-v3-现状对照.md @@ -67,7 +67,7 @@ | 日期 | 说明 | |------|------| -| 2026-08-25 | v3.5.9:门店列表累计核销好客权益;HQ 表格去省略号;主列表序号 + 列设置存 HQ 账号;用户列表双击改昵称、手机号不脱敏 | +| 2026-08-25 | v3.5.9:门店列表累计核销好客权益;HQ 表格去省略号;主列表序号 + 列设置存 HQ 账号;用户列表备注(不改昵称)、手机号不脱敏 | | 2026-08-24 | v3.5.8:HQ 单账号追加/撤销权限;运营改客服;城市门店服务与城市范围;分类删除权限与概览按权限/城市裁剪 | | 2026-08-23 | v3.5.7:门店核销记录时间显示秒;首页「今日到账金额」→「今日核销金额」 | | 2026-08-23 | v3.5.6:开发计划任务导出;核销/财务银行信息与打款凭证;工单图片入任务;运营去调试;门店审核全屏 | diff --git a/docs/杜康好客-v3.5.9-开发文档.md b/docs/杜康好客-v3.5.9-开发文档.md index 4fe0fce..21bad1c 100644 --- a/docs/杜康好客-v3.5.9-开发文档.md +++ b/docs/杜康好客-v3.5.9-开发文档.md @@ -1,7 +1,7 @@ # 杜康好客 · v3.5.9 开发文档 > **2026-08-25** · admin-web / API -> **主题**:门店累计核销好客权益、HQ 表格去省略号、序号列与列设置;用户列表改昵称 / 手机号不脱敏 +> **主题**:门店累计核销好客权益、HQ 表格去省略号、序号列与列设置;用户备注 / 手机号不脱敏 --- @@ -10,7 +10,7 @@ 1. **门店列表**增加「累计核销好客权益」:该店历史核销券面额合计。 2. **全 HQ 表格**去掉 `...` 截断,长文本完整展示,横向滚动。 3. 每张 **主列表**:最左序号(跨页连续);「列设置」弹窗控制显示列与顺序,偏好存当前 HQ 账号。 -4. **用户列表**:双击昵称进入编辑,离开编辑(失焦 / 回车 / Esc)即写入数据库;手机号列表明文,不脱敏。 +4. **用户列表**:昵称只读(用户自己的);新增「备注」列,双击编辑 HQ 标记,离开即写入 `user_user.hq_remark`;手机号列表明文,不脱敏。 **不做**:C 端 / 合伙人 / 门店端;详情 Drawer 内嵌表不加列设置。 @@ -52,20 +52,21 @@ |------|------|------| | GET | `/admin/auth/me` | `listColumnPrefs` | | PUT | `/admin/me/list-columns/:listKey` | `{ order, hidden }` 或 `{ reset: true }`;只改当前账号 | -| PUT | `/admin/users/:id` | `{ nickname }`,空串存 `null` | +| PUT | `/admin/users/:id` | `{ hqRemark }`,空串存 `null`;不改 nickname | `listKey` 白名单见 `packages/shared-types/src/hq-list-columns.ts`。无新权限键。 --- -## 5. 用户列表:改昵称 / 手机号不脱敏 +## 5. 用户列表:备注 / 手机号不脱敏 | 项 | 行为 | |----|------| -| 昵称 | 双击单元格进入输入;失焦、回车或 Esc 退出编辑即 `PUT /admin/users/:id` 保存(空串写成 `null`);最长 64 | +| 昵称 | 只读,来自用户自己的 `nickname`,HQ 不可改 | +| 备注 | 双击单元格进入输入;失焦、回车或 Esc 退出编辑即 `PUT /admin/users/:id` `{ hqRemark }`(空串写成 `null`);最长 128;C 端不展示 | | 手机号 | 列表列明文 `phone`,不再 `maskPhone`。详情抽屉仍脱敏 | -审计:`USER_UPDATE`(修改用户昵称)。无新权限键,已登录 HQ 即可(与用户列表同入口)。 +审计:`USER_UPDATE`(修改用户备注)。无新权限键,已登录 HQ 即可(与用户列表同入口)。 --- @@ -73,7 +74,12 @@ `hq_account.list_column_prefs` JSON NULL。 -脚本:[`server/dukang-api/prisma/migrate-hq-list-column-prefs-v359.sql`](../server/dukang-api/prisma/migrate-hq-list-column-prefs-v359.sql) +`user_user.hq_remark` VARCHAR(128) NULL。 + +脚本: + +- [`migrate-hq-list-column-prefs-v359.sql`](../server/dukang-api/prisma/migrate-hq-list-column-prefs-v359.sql) +- [`migrate-user-hq-remark-v359.sql`](../server/dukang-api/prisma/migrate-user-hq-remark-v359.sql) --- @@ -81,7 +87,7 @@ | 步骤 | 动作 | |------|------| -| 1 | 执行 `migrate-hq-list-column-prefs-v359.sql` 或 `prisma db push` | +| 1 | 执行 `migrate-hq-list-column-prefs-v359.sql`、`migrate-user-hq-remark-v359.sql` 或 `prisma db push` | | 2 | `pnpm build`(shared-types + API + admin-web) | | 3 | 发 `dukang-api` + `admin-web` | @@ -97,7 +103,7 @@ | 表格 hook | `apps/admin-web/src/lib/useAdminListColumns.tsx` | | 弹窗 | `AdminListColumnsModal.tsx` | | 样式 | `admin.css` | -| 改昵称 | `admin-users.service.ts` · `UsersPage.tsx` | +| 备注 | `admin-users.service.ts` · `UsersPage.tsx` | --- @@ -109,5 +115,5 @@ - [ ] 列设置可隐藏/排序;保存后刷新仍在;重置恢复默认 - [ ] 序号、操作列不能在弹窗关掉或拖走 - [ ] 详情描述列表仍可省略 -- [ ] 用户列表双击昵称可改,离开编辑后库中已更新 +- [ ] 用户列表昵称只读;双击「备注」可改,离开编辑后 `user_user.hq_remark` 已更新;C 端看不到该字段 - [ ] 用户列表手机号完整可见(详情仍脱敏) diff --git a/docs/杜康好客-v3编码手册.md b/docs/杜康好客-v3编码手册.md index 3243ac6..b02c39e 100644 --- a/docs/杜康好客-v3编码手册.md +++ b/docs/杜康好客-v3编码手册.md @@ -43,7 +43,7 @@ C 端购酒核销 · 门店扫码核销+打款 · 合伙人拓店履约 · WebAd **HQ 权限(v3.5.8)**:生效 =(角色 ∪ 追加)− 撤销。城市范围绑在账号上(空=全国;「城市门店服务」必须勾城)。门店 API 按 `cityIds` 强制过滤。城市门店服务可新增分类、不可删除;概览按权限与城市范围裁剪。 -**HQ 列表(v3.5.9)**:主表不省略号、可横滑;最左序号;列设置(显隐/顺序)存 `hq_account.list_column_prefs`。门店列表「累计核销好客权益」= 该店 `RedeemRecord.amount` 合计。用户列表双击昵称、离开即保存;列表手机号不脱敏。 +**HQ 列表(v3.5.9)**:主表不省略号、可横滑;最左序号;列设置(显隐/顺序)存 `hq_account.list_column_prefs`。门店列表「累计核销好客权益」= 该店 `RedeemRecord.amount` 合计。用户列表昵称只读;双击「备注」离开即保存(`hq_remark`);列表手机号不脱敏。 ## 5. 验收用例(必过) diff --git a/docs/杜康好客-知识库.md b/docs/杜康好客-知识库.md index ae02b22..2795099 100644 --- a/docs/杜康好客-知识库.md +++ b/docs/杜康好客-知识库.md @@ -88,7 +88,7 @@ | 开城 | 城市、合伙人(全城/区域+佣金)、仓库 | | 商品 | SKU、上下架、详情模板 | | 门店 | 审核、套餐 Tab 直存/审核、合伙人列、累计核销好客权益 | -| 用户 | 列表手机号明文;双击昵称离开即保存 | +| 用户 | 列表手机号明文;备注双击离开即保存(不改昵称) | | 交易 | 订单、权益券、核销记录、推广码+metrics | | 财务 | 门店/合伙人/酒厂/物流账单;打款确认 | | 工单 | 售后四类型 + 技术支持(ST) + 开发计划 | diff --git a/packages/shared-types/src/ops.ts b/packages/shared-types/src/ops.ts index 28710f8..7d00f46 100644 --- a/packages/shared-types/src/ops.ts +++ b/packages/shared-types/src/ops.ts @@ -4,7 +4,7 @@ export interface AdminListQuery { } export interface UpdateAdminUserRequest { - nickname: string; + hqRemark: string; } export interface AdminPageResult { diff --git a/server/dukang-api/prisma/migrate-user-hq-remark-v359.sql b/server/dukang-api/prisma/migrate-user-hq-remark-v359.sql new file mode 100644 index 0000000..14bddba --- /dev/null +++ b/server/dukang-api/prisma/migrate-user-hq-remark-v359.sql @@ -0,0 +1,3 @@ +-- v3.5.9 补丁:HQ 管理员对用户的备注(不改 nickname) +ALTER TABLE `user_user` + ADD COLUMN `hq_remark` VARCHAR(128) NULL AFTER `nickname`; diff --git a/server/dukang-api/prisma/schema.prisma b/server/dukang-api/prisma/schema.prisma index 5a90400..58907d0 100644 --- a/server/dukang-api/prisma/schema.prisma +++ b/server/dukang-api/prisma/schema.prisma @@ -1309,6 +1309,8 @@ model User { wxOpenId String? @map("wx_open_id") @db.VarChar(64) wxUnionId String? @map("wx_union_id") @db.VarChar(64) nickname String? @db.VarChar(64) + /// HQ 管理员备注(C 端不展示、不可改) + hqRemark String? @map("hq_remark") @db.VarChar(128) avatarResourceId BigInt? @map("avatar_resource_id") @db.UnsignedBigInt status Int @default(1) @db.TinyInt /// 测试账号:命中全局测试白名单手机号 diff --git a/server/dukang-api/src/common/hq-operation/hq-operation.constants.ts b/server/dukang-api/src/common/hq-operation/hq-operation.constants.ts index 3648cf1..f93cf59 100644 --- a/server/dukang-api/src/common/hq-operation/hq-operation.constants.ts +++ b/server/dukang-api/src/common/hq-operation/hq-operation.constants.ts @@ -142,7 +142,7 @@ export const HQ_OPERATION_ACTION_LABELS: Record = { [HqOperationAction.HQ_ACCOUNT_CREATE]: '新增 HQ 管理员', [HqOperationAction.HQ_ACCOUNT_UPDATE]: '编辑 HQ 管理员', [HqOperationAction.HQ_PERMISSION_UPDATE]: '配置 HQ 权限', - [HqOperationAction.USER_UPDATE]: '修改用户昵称', + [HqOperationAction.USER_UPDATE]: '修改用户备注', [HqOperationAction.USER_DELETE]: '删除用户', [HqOperationAction.USER_BATCH_DELETE]: '批量删除用户', [HqOperationAction.ORDER_SHIP]: '订单发货', diff --git a/server/dukang-api/src/modules/ops/admin-users.service.ts b/server/dukang-api/src/modules/ops/admin-users.service.ts index b66a577..ce135fa 100644 --- a/server/dukang-api/src/modules/ops/admin-users.service.ts +++ b/server/dukang-api/src/modules/ops/admin-users.service.ts @@ -37,6 +37,7 @@ function mapAdminUserRow(u: { mergedIntoUserId: bigint | null; wxOpenId: string | null; nickname: string | null; + hqRemark: string | null; status: number; sourceType: string; sourceRefId: bigint | null; @@ -56,6 +57,7 @@ function mapAdminUserRow(u: { wxOpenId: u.wxOpenId, wechatVerified: !!u.wxOpenId, nickname: u.nickname, + hqRemark: u.hqRemark, status: u.status, sourceType: u.sourceType, sourceRefId: u.sourceRefId, @@ -100,6 +102,7 @@ export class AdminUsersService { mergedIntoUserId: true, wxOpenId: true, nickname: true, + hqRemark: true, status: true, sourceType: true, sourceRefId: true, @@ -188,14 +191,14 @@ export class AdminUsersService { }); } - async updateUser(id: bigint, dto: { nickname: string }) { + async updateUser(id: bigint, dto: { hqRemark: string }) { const user = await this.prisma.user.findUnique({ where: { id }, select: { id: true } }); if (!user) throw new NotFoundException('用户不存在'); - const nickname = dto.nickname.trim() || null; + const hqRemark = dto.hqRemark.trim() || null; const updated = await this.prisma.user.update({ where: { id }, - data: { nickname }, - select: { id: true, userNo: true, nickname: true }, + data: { hqRemark }, + select: { id: true, userNo: true, hqRemark: true }, }); return serializeBigInt(updated); } diff --git a/server/dukang-api/src/modules/ops/dto/admin-mutate.dto.ts b/server/dukang-api/src/modules/ops/dto/admin-mutate.dto.ts index 9abf5cf..5f7a494 100644 --- a/server/dukang-api/src/modules/ops/dto/admin-mutate.dto.ts +++ b/server/dukang-api/src/modules/ops/dto/admin-mutate.dto.ts @@ -951,8 +951,8 @@ export class BatchDeleteUsersConfirmDto extends BatchDeleteUsersDto { export class UpdateAdminUserDto { @IsString() - @MaxLength(64) - nickname: string; + @MaxLength(128) + hqRemark: string; } /** HQ 订单发货(目前仅小飞侠 XFX) */