H5端开店增加获取当前店铺位置

This commit is contained in:
2026-07-27 22:37:15 +08:00
parent 23c0cc7a5f
commit 09eda9a11e
9 changed files with 253 additions and 5 deletions
@@ -268,6 +268,18 @@ export class AdminStoresService {
const categoryId = BigInt(dto.categoryId);
await this.storeCategoryService.assertLeafCategoryId(categoryId);
const latitude = dto.latitude != null ? Number(dto.latitude) : null;
const longitude = dto.longitude != null ? Number(dto.longitude) : null;
if ((latitude == null) !== (longitude == null)) {
throw new BadRequestException('经纬度须同时提供');
}
if (
latitude != null &&
(!Number.isFinite(latitude) || !Number.isFinite(longitude!) || latitude < -90 || latitude > 90)
) {
throw new BadRequestException('经纬度无效');
}
const openTime = dto.openTime?.trim() || '10:00';
const closeTime = dto.closeTime?.trim() || '22:00';
const openTime2 = dto.openTime2?.trim() || '';
@@ -296,6 +308,7 @@ export class AdminStoresService {
closeTime,
openTime2: openTime2 || null,
closeTime2: closeTime2 || null,
...(latitude != null && longitude != null ? { latitude, longitude } : {}),
status: 'OPEN',
auditStatus: 'APPROVED',
auditedAt: new Date(),
@@ -57,6 +57,14 @@ export class CreateStoreDto {
@IsNotEmpty()
address: string;
@IsOptional()
@IsNumber()
latitude?: number;
@IsOptional()
@IsNumber()
longitude?: number;
@IsOptional()
@IsString()
intro?: string;
@@ -28,6 +28,15 @@ function haversineMeters(lat1: number, lng1: number, lat2: number, lng2: number)
return 2 * R * Math.asin(Math.min(1, Math.sqrt(a)));
}
function parseOptionalCoord(value: unknown, kind: 'lat' | 'lng' = 'lng'): number | null {
if (value == null || value === '') return null;
const n = typeof value === 'number' ? value : Number(value);
if (!Number.isFinite(n)) return null;
if (kind === 'lat' && (n < -90 || n > 90)) return null;
if (kind === 'lng' && (n < -180 || n > 180)) return null;
return n;
}
@Injectable()
export class StoreService {
private readonly config = loadAppConfig();
@@ -296,6 +305,12 @@ export class StoreService {
throw new BadRequestException('门店简介须为 2~500 字');
}
const latitude = parseOptionalCoord(body.latitude, 'lat');
const longitude = parseOptionalCoord(body.longitude, 'lng');
if ((latitude == null) !== (longitude == null)) {
throw new BadRequestException('经纬度须同时提供');
}
const store = await this.prisma.store.create({
data: {
cityId: city.id,
@@ -313,6 +328,7 @@ export class StoreService {
closeTime,
openTime2: openTime2 || null,
closeTime2: closeTime2 || null,
...(latitude != null && longitude != null ? { latitude, longitude } : {}),
status: this.config.autoApproveStore ? 'OPEN' : 'PAUSED',
auditStatus: this.config.autoApproveStore ? 'APPROVED' : 'PENDING',
auditedAt: this.config.autoApproveStore ? new Date() : null,
@@ -320,7 +336,9 @@ export class StoreService {
},
});
await this.ensureStoreCoordinates(store);
if (latitude == null || longitude == null) {
await this.ensureStoreCoordinates(store);
}
const ossBucket = process.env.OSS_BUCKET ?? 'legacy';
@@ -500,6 +518,10 @@ export class StoreService {
const phone = body.phone !== undefined ? String(body.phone).trim() : undefined;
const address = body.address !== undefined ? String(body.address).trim() : undefined;
const introRaw = body.intro !== undefined ? String(body.intro).trim() : undefined;
const latitude =
body.latitude !== undefined ? parseOptionalCoord(body.latitude, 'lat') : undefined;
const longitude =
body.longitude !== undefined ? parseOptionalCoord(body.longitude, 'lng') : undefined;
if (name !== undefined && !name) throw new BadRequestException('请填写门店名称');
if (phone !== undefined && !/^1\d{10}$/.test(phone)) {
@@ -509,16 +531,26 @@ export class StoreService {
if (introRaw && (introRaw.length < 2 || introRaw.length > 500)) {
throw new BadRequestException('门店简介须为 2~500 字');
}
if (
(latitude !== undefined || longitude !== undefined) &&
(latitude == null || longitude == null)
) {
throw new BadRequestException('经纬度须同时提供');
}
const resubmitAudit = store.auditStatus === 'REJECTED';
const hasCoordsUpdate = latitude != null && longitude != null;
const updated = await this.prisma.store.update({
where: { id: storeId },
data: {
...(name !== undefined ? { name } : {}),
...(phone !== undefined ? { phone } : {}),
...(address !== undefined
? { address, latitude: null, longitude: null }
? hasCoordsUpdate
? { address }
: { address, latitude: null, longitude: null }
: {}),
...(hasCoordsUpdate ? { latitude, longitude } : {}),
...(introRaw !== undefined ? { intro: introRaw || null } : {}),
...(resubmitAudit
? {
@@ -531,7 +563,7 @@ export class StoreService {
},
});
if (address !== undefined) {
if (address !== undefined && !hasCoordsUpdate) {
await this.ensureStoreCoordinates(updated);
}