feat(store): separate login phone from public contactPhone

Store.phone stays login/boss; contactPhone is C-end dial. Public store APIs keep phone compat via publicDial mapping.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-09 07:50:40 +08:00
parent a598d1cd30
commit 07630c9046
11 changed files with 265 additions and 67 deletions
@@ -146,8 +146,10 @@ export class AdminStoresService {
visibilityPhones: visibilityPhones.map((p) => p.phone),
partner: store.partnerAccount,
account: store.bindings[0]?.storeAccount ?? null,
/** 门店端登录手机号(store_account.phone与 store.phone 对齐 */
/** 门店端登录手机号(store_account.phone与 store.phone 对齐 */
loginPhone: store.bindings[0]?.storeAccount?.phone ?? store.phone,
/** 对外联系电话;空则回退登录号 */
contactPhone: store.contactPhone?.trim() || store.phone,
bindings: undefined,
media,
audits,
@@ -275,11 +277,19 @@ export class AdminStoresService {
if (dto.phone !== undefined) {
const normalizedPhone = dto.phone.trim();
if (!/^1[3-9]\d{9}$/.test(normalizedPhone)) {
throw new BadRequestException('请输入正确的手机号');
throw new BadRequestException('请输入正确的登录手机号');
}
}
if (dto.contactPhone !== undefined) {
const contact = dto.contactPhone.trim();
if (contact && !/^1[3-9]\d{9}$/.test(contact)) {
throw new BadRequestException('请输入正确的联系电话');
}
}
const normalizedPhone = dto.phone !== undefined ? dto.phone.trim() : undefined;
const normalizedContactPhone =
dto.contactPhone !== undefined ? dto.contactPhone.trim() || null : undefined;
if (dto.visibilityWhitelistEnabled !== undefined || dto.visibilityPhones !== undefined) {
const nextEnabled =
dto.visibilityWhitelistEnabled !== undefined
@@ -296,7 +306,7 @@ export class AdminStoresService {
const needAccountSync =
normalizedPhone !== undefined || dto.name !== undefined || bankTouched;
// 登录凭证在 store_account.phone必须与门店展示手机号同步
// 登录凭证在 store_account.phone与 store.phone(老板登录号)同步,不与对外联系电话绑定
let primaryBinding: {
storeAccount: { id: bigint; phone: string; name: string } | null;
} | null = null;
@@ -346,6 +356,9 @@ export class AdminStoresService {
data: {
...(dto.name !== undefined ? { name: dto.name.trim() } : {}),
...(normalizedPhone !== undefined ? { phone: normalizedPhone } : {}),
...(normalizedContactPhone !== undefined
? { contactPhone: normalizedContactPhone }
: {}),
...(dto.intro !== undefined ? { intro: dto.intro } : {}),
...(dto.benefitUsageRule !== undefined
? { benefitUsageRule: normalizeStoreOptionalText(dto.benefitUsageRule) }
@@ -562,6 +575,11 @@ export class AdminStoresService {
? !!dto.isTest
: await this.testWhitelist.isPhoneInWhitelist(normalizedPhone);
const contactPhoneRaw = dto.contactPhone?.trim() || normalizedPhone;
if (!/^1[3-9]\d{9}$/.test(contactPhoneRaw)) {
throw new BadRequestException('请输入正确的联系电话');
}
const store = await this.prisma.store.create({
data: {
cityId: city.id,
@@ -570,6 +588,7 @@ export class AdminStoresService {
categoryId,
name: dto.name,
phone: normalizedPhone,
contactPhone: contactPhoneRaw,
province: dto.province ?? city.province,
cityName: dto.city ?? city.name,
district: dto.district ?? '',