Files
dukang/server/dukang-api/src/common/system-config/system-config.registry.ts
T
jacy fc2e5b65de
CI / verify (pull_request) Has been cancelled
v3.5.3版本更新1
2026-08-20 18:54:15 +08:00

618 lines
20 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 type { SystemConfigFieldMeta, SystemConfigGroupMeta } from '@dukang/shared-types';
import {
BRAND_LOGO_MARK_URL,
BRAND_LOGO_OSS_BASE,
BRAND_LOGO_URL,
BRAND_LOGO_WIDE_URL,
CUSTOMER_SERVICE_PHONE,
DEFAULT_SHARE_BENEFIT_TITLE,
DEFAULT_SHARE_DESC,
DEFAULT_SHARE_HINT,
DEFAULT_SHARE_MINE_TITLE,
DEFAULT_SHARE_ORDER_TITLE,
DEFAULT_SHARE_STORES_TITLE,
DEFAULT_SHARE_TITLE,
DEFAULT_USER_H5_URL,
MINI_USER_STATIC_OSS_BASE,
MOCK_SMS_FIXED_CODE,
QUALIFICATION_DISCLOSURE_URL,
} from '@dukang/shared-types';
/** 运行环境、安全与鉴权仅保留在 .env,不在 HQ 系统设置中维护 */
export const SYSTEM_CONFIG_GROUPS: SystemConfigGroupMeta[] = [
{ key: 'feature', label: '功能开关' },
{ key: 'sms', label: '短信' },
{ key: 'wechat', label: '微信' },
{ key: 'wechat_mini', label: '微信小程序配置' },
{ key: 'wechat_mini_share', label: '小程序分享配置' },
{ key: 'oss', label: '对象存储 OSS' },
{ key: 'app', label: '应用链接' },
{ key: 'deploy', label: '发布部署' },
{ key: 'winery_bank', label: '酒厂银行账户' },
{ key: 'finance', label: '财务结算' },
];
const G = {
feature: 'feature',
sms: 'sms',
wechat: 'wechat',
wechat_mini: 'wechat_mini',
wechat_mini_share: 'wechat_mini_share',
oss: 'oss',
app: 'app',
deploy: 'deploy',
winery_bank: 'winery_bank',
finance: 'finance',
} as const;
/** HQ 可维护字段(不含 NODE_ENV / DATABASE_URL / JWT 等基础设施项) */
export const SYSTEM_CONFIG_FIELDS: SystemConfigFieldMeta[] = [
{
key: 'MOCK_SMS',
label: 'Mock 短信',
group: G.feature,
type: 'boolean',
requiresRestart: false,
description: '开启后不发真实短信,验证码为随机 6 位数字,并记录在下方列表',
},
{
key: 'MOCK_PAY',
label: 'Mock 支付',
group: G.feature,
type: 'boolean',
requiresRestart: false,
description: '关闭且已配置微信商户参数时走真实 JSAPI 支付',
},
{
key: 'MOCK_WECHAT',
label: 'Mock 微信授权',
group: G.feature,
type: 'boolean',
requiresRestart: false,
description: '开启走 Mock OAuth/登录;关闭且已配置 WX_APP_ID/SECRET 时走真实微信',
},
{ key: 'MOCK_DELIVERY_AUTO', label: 'Mock 配送自动完成', group: G.feature, type: 'boolean', requiresRestart: false },
{ key: 'AUTO_APPROVE_STORE', label: '门店自动审核通过', group: G.feature, type: 'boolean', requiresRestart: false },
{
key: 'WECOM_AIBOT_ENABLED',
label: '启用企微机器人长连接',
group: G.feature,
type: 'boolean',
requiresRestart: false,
description: '总开关。开启后连接 HQ「企微机器人 → 智能机器人」中已启用且配置完整的 Bot(每 Bot 同时仅 1 条长连接)',
},
{ key: 'ALIYUN_SMS_SIGN_NAME', label: '短信签名', group: G.sms, type: 'string', requiresRestart: false },
{ key: 'ALIYUN_SMS_TEMPLATE_CODE', label: '默认短信模板', group: G.sms, type: 'string', requiresRestart: false },
{ key: 'ALIYUN_SMS_TEMPLATE_CONFIRM_REDEEM', label: '核销确认模板', group: G.sms, type: 'string', requiresRestart: false },
{ key: 'ALIYUN_SMS_TEMPLATE_PROXY_ORDER', label: '代下单确认模板', group: G.sms, type: 'string', requiresRestart: false },
{ key: 'ALIYUN_SMS_ACCESS_KEY_ID', label: '短信 AccessKey ID', group: G.sms, type: 'password', secret: true, requiresRestart: true },
{ key: 'ALIYUN_SMS_ACCESS_KEY_SECRET', label: '短信 AccessKey Secret', group: G.sms, type: 'password', secret: true, requiresRestart: true },
{ key: 'WX_APP_ID', label: '服务号 AppID', group: G.wechat, type: 'string', requiresRestart: true },
{ key: 'WX_APP_SECRET', label: '服务号 AppSecret', group: G.wechat, type: 'password', secret: true, requiresRestart: true },
{ key: 'WX_MINI_APP_ID', label: '小程序 AppID', group: G.wechat, type: 'string', requiresRestart: true },
{ key: 'WX_MINI_APP_SECRET', label: '小程序 AppSecret', group: G.wechat, type: 'password', secret: true, requiresRestart: true },
{ key: 'WX_MCH_ID', label: '微信商户号', group: G.wechat, type: 'string', requiresRestart: true },
{ key: 'WX_MCH_SERIAL_NO', label: '商户证书序列号', group: G.wechat, type: 'string', requiresRestart: true },
{ key: 'WX_MCH_PRIVATE_KEY', label: '商户私钥 PEM', group: G.wechat, type: 'textarea', secret: true, requiresRestart: true },
{ key: 'WX_API_V3_KEY', label: 'APIv3 密钥', group: G.wechat, type: 'password', secret: true, requiresRestart: true },
{ key: 'WX_PLATFORM_CERT', label: '微信平台公钥证书', group: G.wechat, type: 'textarea', secret: true, requiresRestart: true },
{ key: 'WX_PAY_NOTIFY_URL', label: '支付回调 URL', group: G.wechat, type: 'string', requiresRestart: false },
{ key: 'WX_REFUND_NOTIFY_URL', label: '退款回调 URL', group: G.wechat, type: 'string', requiresRestart: false },
{
key: 'WX_MINI_MSG_TOKEN',
label: '小程序消息推送 Token',
group: G.wechat,
type: 'password',
secret: true,
requiresRestart: true,
description:
'小程序后台「开发-开发管理-消息推送」Token;回调 URL=/api/v1/callbacks/wechat/message',
},
{
key: 'WX_MINI_MSG_AES_KEY',
label: '小程序消息推送 EncodingAESKey',
group: G.wechat,
type: 'password',
secret: true,
requiresRestart: true,
description: '消息推送安全模式 EncodingAESKey43 位);明文模式可留空',
},
{
key: 'MINI_HOME_BANNERS',
label: '首页轮播图',
group: G.wechat_mini,
type: 'imageList',
requiresRestart: false,
description: '小程序商品首页顶部轮播,建议比例 15:8,最多 8 张;上传后需点击右上角「保存」',
},
{
key: 'MINI_HOME_FOOTER_URL',
label: '首页底部图',
group: G.wechat_mini,
type: 'image',
requiresRestart: false,
description: '小程序商品首页底部 footer,建议比例 15:4;上传后需点击右上角「保存」',
},
{
key: 'MINI_USER_MIN_VERSION',
label: '小程序最低版本',
group: G.wechat_mini,
type: 'string',
requiresRestart: false,
placeholder: '3.4.15',
description: 'semver 格式;客户端低于此版本时提示更新',
},
{
key: 'USER_H5_URL',
label: 'C 端 H5 落地页',
group: G.wechat_mini,
type: 'string',
requiresRestart: false,
placeholder: 'https://user.example.com/user',
description: '推广码二维码 / 未配置时的默认落地页前缀(无末尾斜杠)',
},
{
key: 'BRAND_LOGO_OSS_BASE',
label: '品牌 Logo OSS 根路径',
group: G.wechat_mini,
type: 'string',
requiresRestart: false,
placeholder: 'https://xxx.oss-cn-beijing.aliyuncs.com/logo/',
description: '仅作说明/备份;下方三张 Logo 请直接配置完整 URL',
},
{
key: 'BRAND_LOGO_URL',
label: '方形 Logo',
group: G.wechat_mini,
type: 'image',
requiresRestart: false,
description: '品牌方形 Logo;分享默认图未单独配置时回退此图',
},
{
key: 'BRAND_LOGO_WIDE_URL',
label: '长方形 Logo',
group: G.wechat_mini,
type: 'image',
requiresRestart: false,
description: '含文字,登录页等场景',
},
{
key: 'BRAND_LOGO_MARK_URL',
label: '图标 Logo',
group: G.wechat_mini,
type: 'image',
requiresRestart: false,
description: '默认头像(未微信授权时)',
},
{
key: 'MINI_USER_STATIC_OSS_BASE',
label: '小程序静态资源 OSS 根路径',
group: G.wechat_mini,
type: 'string',
requiresRestart: false,
placeholder: 'https://xxx.oss-cn-beijing.aliyuncs.com/static/mini-user/',
description: '资质公示等静态资源根路径;完整 URL 优先用下方「资质公示图」',
},
{
key: 'QUALIFICATION_DISCLOSURE_URL',
label: '资质公示长图',
group: G.wechat_mini,
type: 'image',
requiresRestart: false,
description: '「我的」页资质公示大图',
},
{
key: 'CUSTOMER_SERVICE_PHONE',
label: '总部客服电话',
group: G.wechat_mini,
type: 'string',
requiresRestart: false,
placeholder: '13203801799',
description: 'C 端联系客服拨号号码',
},
{
key: 'PARTNER_ONBOARD_CS_QR_URL',
label: '合伙人入驻 · 企微客服二维码',
group: G.wechat_mini,
type: 'image',
requiresRestart: false,
description: '合伙人 H5 录入门店提交前展示;未配置时禁止提交入驻',
},
{
key: 'PARTNER_ONBOARD_CS_HINT',
label: '合伙人入驻 · 客服提示文案',
group: G.wechat_mini,
type: 'string',
requiresRestart: false,
placeholder: '使用问题、提现问题等随时可联系【杜康好客】客服',
description: '二维码下方说明;留空用默认文案',
},
{
key: 'MOCK_SMS_FIXED_CODE',
label: 'Mock 短信固定验证码',
group: G.wechat_mini,
type: 'string',
requiresRestart: false,
placeholder: '999888',
description: '仅 MOCK_SMS 开启时生效;留空则用默认 999888',
},
// —— 小程序分享配置 ——
{
key: 'SHARE_HINT',
label: '分享引导文案',
group: G.wechat_mini_share,
subgroup: 'global',
type: 'string',
requiresRestart: false,
placeholder: DEFAULT_SHARE_HINT,
description: '点击分享按钮后的 Toast 提示(小程序内)',
},
{
key: 'SHARE_DEFAULT_TITLE',
label: '默认分享标题',
group: G.wechat_mini_share,
subgroup: 'global',
type: 'string',
requiresRestart: false,
placeholder: DEFAULT_SHARE_TITLE,
description: '各场景未单独配置标题时使用',
},
{
key: 'SHARE_DEFAULT_DESC',
label: '默认分享描述',
group: G.wechat_mini_share,
subgroup: 'global',
type: 'string',
requiresRestart: false,
placeholder: DEFAULT_SHARE_DESC,
description: 'H5 分享卡片描述;小程序朋友圈/好友卡片副文案回退',
},
{
key: 'SHARE_DEFAULT_IMAGE_URL',
label: '默认分享图',
group: G.wechat_mini_share,
subgroup: 'global',
type: 'image',
requiresRestart: false,
description: '各场景未配置图且无业务图时使用;建议接近 5:4',
},
{
key: 'SHARE_HOME_TITLE',
label: '标题',
group: G.wechat_mini_share,
subgroup: 'home',
type: 'string',
requiresRestart: false,
},
{
key: 'SHARE_HOME_DESC',
label: '描述',
group: G.wechat_mini_share,
subgroup: 'home',
type: 'string',
requiresRestart: false,
},
{
key: 'SHARE_HOME_IMAGE_URL',
label: '分享图',
group: G.wechat_mini_share,
subgroup: 'home',
type: 'image',
requiresRestart: false,
description: '留空则优先用首页首张轮播图,再回退默认分享图',
},
{
key: 'SHARE_STORES_TITLE',
label: '标题',
group: G.wechat_mini_share,
subgroup: 'stores',
type: 'string',
requiresRestart: false,
placeholder: DEFAULT_SHARE_STORES_TITLE,
},
{
key: 'SHARE_STORES_DESC',
label: '描述',
group: G.wechat_mini_share,
subgroup: 'stores',
type: 'string',
requiresRestart: false,
},
{
key: 'SHARE_STORES_IMAGE_URL',
label: '分享图',
group: G.wechat_mini_share,
subgroup: 'stores',
type: 'image',
requiresRestart: false,
},
{
key: 'SHARE_STORE_DETAIL_TITLE',
label: '标题',
group: G.wechat_mini_share,
subgroup: 'storeDetail',
type: 'string',
requiresRestart: false,
description: '小程序好友/朋友圈分享固定用门店名称;本项仅作其它端回退参考,可留空',
},
{
key: 'SHARE_STORE_DETAIL_DESC',
label: '描述',
group: G.wechat_mini_share,
subgroup: 'storeDetail',
type: 'string',
requiresRestart: false,
description: '留空则用门店简介/地址',
},
{
key: 'SHARE_STORE_DETAIL_IMAGE_URL',
label: '分享图',
group: G.wechat_mini_share,
subgroup: 'storeDetail',
type: 'image',
requiresRestart: false,
description: '小程序分享固定用门头图/环境图;本项可留空',
},
{
key: 'SHARE_BENEFIT_TITLE',
label: '标题',
group: G.wechat_mini_share,
subgroup: 'benefit',
type: 'string',
requiresRestart: false,
placeholder: DEFAULT_SHARE_BENEFIT_TITLE,
},
{
key: 'SHARE_BENEFIT_DESC',
label: '描述',
group: G.wechat_mini_share,
subgroup: 'benefit',
type: 'string',
requiresRestart: false,
},
{
key: 'SHARE_BENEFIT_IMAGE_URL',
label: '分享图',
group: G.wechat_mini_share,
subgroup: 'benefit',
type: 'image',
requiresRestart: false,
},
{
key: 'SHARE_MINE_TITLE',
label: '标题',
group: G.wechat_mini_share,
subgroup: 'mine',
type: 'string',
requiresRestart: false,
placeholder: DEFAULT_SHARE_MINE_TITLE,
},
{
key: 'SHARE_MINE_DESC',
label: '描述',
group: G.wechat_mini_share,
subgroup: 'mine',
type: 'string',
requiresRestart: false,
},
{
key: 'SHARE_MINE_IMAGE_URL',
label: '分享图',
group: G.wechat_mini_share,
subgroup: 'mine',
type: 'image',
requiresRestart: false,
},
{
key: 'SHARE_PRODUCT_DETAIL_TITLE',
label: '标题',
group: G.wechat_mini_share,
subgroup: 'productDetail',
type: 'string',
requiresRestart: false,
description: '小程序好友/朋友圈分享固定用商品名称;本项可留空',
},
{
key: 'SHARE_PRODUCT_DETAIL_DESC',
label: '描述',
group: G.wechat_mini_share,
subgroup: 'productDetail',
type: 'string',
requiresRestart: false,
description: '留空则用商品副标题',
},
{
key: 'SHARE_PRODUCT_DETAIL_IMAGE_URL',
label: '分享图',
group: G.wechat_mini_share,
subgroup: 'productDetail',
type: 'image',
requiresRestart: false,
description: '小程序分享固定用商品主图;本项可留空',
},
{
key: 'SHARE_ORDER_DETAIL_TITLE',
label: '标题',
group: G.wechat_mini_share,
subgroup: 'orderDetail',
type: 'string',
requiresRestart: false,
placeholder: DEFAULT_SHARE_ORDER_TITLE,
description: '支持 {productName} 占位符',
},
{
key: 'SHARE_ORDER_DETAIL_DESC',
label: '描述',
group: G.wechat_mini_share,
subgroup: 'orderDetail',
type: 'string',
requiresRestart: false,
},
{
key: 'SHARE_ORDER_DETAIL_IMAGE_URL',
label: '分享图',
group: G.wechat_mini_share,
subgroup: 'orderDetail',
type: 'image',
requiresRestart: false,
},
{ key: 'OSS_ACCESS_KEY_ID', label: 'OSS AccessKey ID', group: G.oss, type: 'password', secret: true, requiresRestart: true },
{ key: 'OSS_ACCESS_KEY_SECRET', label: 'OSS AccessKey Secret', group: G.oss, type: 'password', secret: true, requiresRestart: true },
{ key: 'OSS_BUCKET', label: 'OSS Bucket', group: G.oss, type: 'string', requiresRestart: true },
{ key: 'OSS_REGION', label: 'OSS Region', group: G.oss, type: 'string', requiresRestart: true, placeholder: 'oss-cn-hangzhou' },
{ key: 'OSS_ENDPOINT', label: 'OSS Endpoint', group: G.oss, type: 'string', requiresRestart: true, description: '可选,内网 endpoint' },
{ key: 'OSS_AUTHORIZATION_V4', label: 'OSS V4 签名', group: G.oss, type: 'boolean', requiresRestart: true },
{ key: 'OSS_CDN_BASE', label: 'OSS 公网域名', group: G.oss, type: 'string', requiresRestart: false },
{ key: 'OSS_UPLOAD_PREFIX', label: '上传前缀', group: G.oss, type: 'string', requiresRestart: false, placeholder: 'uploads' },
{ key: 'OSS_UPLOAD_EXPIRE_SECONDS', label: '直传凭证有效期(秒)', group: G.oss, type: 'number', requiresRestart: false },
{ key: 'OSS_MAX_UPLOAD_BYTES', label: '单文件上限(字节)', group: G.oss, type: 'number', requiresRestart: false },
{
key: 'TENCENT_LBS_KEY',
label: '腾讯位置服务 Key',
group: G.app,
type: 'password',
secret: true,
requiresRestart: false,
description: '须开启 WebServiceAPI;服务端地理编码/地点搜索与前端选点组件共用',
},
{
key: 'TENCENT_LBS_SECRET_KEY',
label: '腾讯位置服务 SecretKeySK',
group: G.app,
type: 'password',
secret: true,
requiresRestart: false,
description:
'控制台开启 WebServiceAPI「签名校验」后生成;仅服务端计算 sig,勿泄露。配置后接口请求自动附带签名',
},
{ key: 'DEPLOY_WEBHOOK_URL', label: '发布 Webhook URL', group: G.deploy, type: 'string', requiresRestart: false },
{ key: 'DEPLOY_WEBHOOK_SECRET', label: '发布 Webhook Secret', group: G.deploy, type: 'password', secret: true, requiresRestart: false },
{
key: 'SENTRY_DSN',
label: 'Sentry DSN',
group: G.deploy,
type: 'password',
secret: true,
requiresRestart: true,
description: '后端错误聚合;留空不启用。配置后需重启 API 生效',
},
{
key: 'WINERY_BANK_ACCOUNT_NAME',
label: '户名',
group: G.winery_bank,
type: 'string',
requiresRestart: false,
description: '酒厂收款账户户名,打款时对照',
},
{
key: 'WINERY_BANK_NAME',
label: '开户银行',
group: G.winery_bank,
type: 'string',
requiresRestart: false,
},
{
key: 'WINERY_BANK_BRANCH',
label: '开户支行',
group: G.winery_bank,
type: 'string',
requiresRestart: false,
placeholder: '可选',
},
{
key: 'WINERY_BANK_ACCOUNT_NO',
label: '银行账号',
group: G.winery_bank,
type: 'string',
requiresRestart: false,
},
{
key: 'STORE_WITHDRAW_DAILY_LIMIT',
label: '门店未出账提现单日上限(元)',
group: G.finance,
type: 'number',
requiresRestart: false,
description: 'FIN-002:单店单日提现上限,默认 5000',
placeholder: '5000',
},
];
/** 已从 HQ 配置移除、仅保留在 .env 的键(启动时从 DB 清理) */
export const SYSTEM_CONFIG_RETIRED_KEYS = [
'NODE_ENV',
'DATABASE_URL',
'REDIS_URL',
'PORT',
'TRUST_PROXY',
'JWT_SECRET',
'JWT_EXPIRES_IN',
'MOCK_SMS_CODE',
'WX_AUTHORIZE',
'WECHAT_AUTH_ENABLED',
'WECHAT_PAY_ENABLED',
'OSS_ENABLED',
'WECOM_AIBOT_BOT_ID',
'WECOM_AIBOT_SECRET',
'WECOM_AIBOT_WELCOME',
'WECOM_BOT_CS_ENABLED',
'WECOM_BOT_CS_BOT_ID',
'WECOM_BOT_CS_SECRET',
'WECOM_BOT_CS_WELCOME',
'WECOM_BOT_CS_PERMISSIONS',
'WECOM_BOT_TECH_ENABLED',
'WECOM_BOT_TECH_BOT_ID',
'WECOM_BOT_TECH_SECRET',
'WECOM_BOT_TECH_WELCOME',
'WECOM_BOT_TECH_PERMISSIONS',
'WECOM_BOT_TEAM_ENABLED',
'WECOM_BOT_TEAM_BOT_ID',
'WECOM_BOT_TEAM_SECRET',
'WECOM_BOT_TEAM_WELCOME',
'WECOM_BOT_TEAM_PERMISSIONS',
] as const;
export const SYSTEM_CONFIG_KEY_SET = new Set(SYSTEM_CONFIG_FIELDS.map((f) => f.key));
/** 表单空值时展示 / 启动补种的默认值(与 shared-types 常量对齐) */
export const SYSTEM_CONFIG_DEFAULTS: Record<string, string> = {
USER_H5_URL: DEFAULT_USER_H5_URL.replace(/\/$/, ''),
BRAND_LOGO_OSS_BASE: BRAND_LOGO_OSS_BASE,
BRAND_LOGO_URL: BRAND_LOGO_URL,
BRAND_LOGO_WIDE_URL: BRAND_LOGO_WIDE_URL,
BRAND_LOGO_MARK_URL: BRAND_LOGO_MARK_URL,
MINI_USER_STATIC_OSS_BASE: MINI_USER_STATIC_OSS_BASE,
QUALIFICATION_DISCLOSURE_URL: QUALIFICATION_DISCLOSURE_URL,
CUSTOMER_SERVICE_PHONE: CUSTOMER_SERVICE_PHONE,
MOCK_SMS_FIXED_CODE: MOCK_SMS_FIXED_CODE,
SHARE_HINT: DEFAULT_SHARE_HINT,
SHARE_DEFAULT_TITLE: DEFAULT_SHARE_TITLE,
SHARE_DEFAULT_DESC: DEFAULT_SHARE_DESC,
SHARE_DEFAULT_IMAGE_URL: BRAND_LOGO_URL,
SHARE_HOME_TITLE: DEFAULT_SHARE_TITLE,
SHARE_HOME_DESC: DEFAULT_SHARE_DESC,
SHARE_STORES_TITLE: DEFAULT_SHARE_STORES_TITLE,
SHARE_STORES_DESC: DEFAULT_SHARE_DESC,
SHARE_BENEFIT_TITLE: DEFAULT_SHARE_BENEFIT_TITLE,
SHARE_BENEFIT_DESC: DEFAULT_SHARE_DESC,
SHARE_MINE_TITLE: DEFAULT_SHARE_MINE_TITLE,
SHARE_MINE_DESC: DEFAULT_SHARE_DESC,
SHARE_ORDER_DETAIL_TITLE: DEFAULT_SHARE_ORDER_TITLE,
SHARE_ORDER_DETAIL_DESC: DEFAULT_SHARE_DESC,
};
export function getSystemConfigField(key: string): SystemConfigFieldMeta | undefined {
return SYSTEM_CONFIG_FIELDS.find((f) => f.key === key);
}
export function getSystemConfigDefault(key: string): string {
return SYSTEM_CONFIG_DEFAULTS[key] ?? '';
}