feat(v3.4.14): mini-user store UX, brand config, client settings

Store list/detail package flow, configurable WeChat mini brand assets and customer service, shop H5 home refresh.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-06 15:21:59 +08:00
parent 53a79d1255
commit a2abbb6169
29 changed files with 1408 additions and 513 deletions
@@ -8,6 +8,7 @@ import type {
import { loadAppConfig, parseMiniHomeBanners, serializeMiniHomeBanners } from '@dukang/shared-types';
import { PrismaService } from '../prisma/prisma.module';
import {
SYSTEM_CONFIG_DEFAULTS,
SYSTEM_CONFIG_FIELDS,
SYSTEM_CONFIG_GROUPS,
SYSTEM_CONFIG_KEY_SET,
@@ -42,6 +43,7 @@ export class SystemConfigService implements OnModuleInit {
try {
await this.purgeRetiredKeys();
await this.seedMissingFromProcessEnv();
await this.seedMissingDefaults();
const rows = await this.prisma.systemConfig.findMany();
applyEnvOverlay(Object.fromEntries(rows.map((r) => [r.configKey, r.value])));
this.lastUpdatedAt = rows.reduce<Date | null>(
@@ -82,7 +84,7 @@ export class SystemConfigService implements OnModuleInit {
for (const field of fields) {
const fromDb = dbMap.get(field.key);
const fromEnv = process.env[field.key];
const raw = fromDb ?? fromEnv ?? '';
const raw = (fromDb ?? fromEnv ?? SYSTEM_CONFIG_DEFAULTS[field.key] ?? '').trim();
if (field.secret) {
if (raw) configuredSecrets.push(field.key);
values[field.key] = '';
@@ -227,6 +229,26 @@ export class SystemConfigService implements OnModuleInit {
}
}
/** 将代码默认值写入空缺配置,便于 HQ 表单可见、可改 */
private async seedMissingDefaults() {
const overlay: Record<string, string> = {};
for (const field of SYSTEM_CONFIG_FIELDS) {
const defaultVal = SYSTEM_CONFIG_DEFAULTS[field.key];
if (!defaultVal?.trim()) continue;
const existing = await this.prisma.systemConfig.findUnique({ where: { configKey: field.key } });
if (existing?.value?.trim()) continue;
if (process.env[field.key]?.trim()) continue;
const value = this.normalizeByMeta(field, defaultVal);
await this.prisma.systemConfig.upsert({
where: { configKey: field.key },
create: { configKey: field.key, value },
update: { value },
});
overlay[field.key] = value;
}
if (Object.keys(overlay).length) applyEnvOverlay(overlay);
}
private normalizeByMeta(meta: { key?: string; type: string }, raw: string): string {
if (meta.type === 'boolean') {
return raw === 'true' || raw === '1' ? 'true' : 'false';