feat;提交管理端和城市合伙人端

This commit is contained in:
ljy
2026-07-05 23:48:20 +08:00
parent fecfc61ec5
commit 569becedaa
73 changed files with 10150 additions and 80 deletions
@@ -7,6 +7,7 @@ import { PayWechatProvider } from './pay/pay.wechat.provider';
import { DeliveryMockProvider } from './delivery/delivery.mock.provider';
import { WechatApiProvider } from './wechat/wechat.api.provider';
import { WechatDisabledProvider } from './wechat/wechat.disabled.provider';
import { WechatMockProvider } from './wechat/wechat.mock.provider';
import { OssMockProvider } from './oss/oss.mock.provider';
import { OssAliyunProvider } from './oss/oss.aliyun.provider';
import {
@@ -27,16 +28,24 @@ import type { IOssProvider } from './oss/oss.interface';
{ provide: SMS_PROVIDER, useClass: SmsMockProvider },
WechatApiProvider,
WechatDisabledProvider,
WechatMockProvider,
{
provide: WECHAT_PROVIDER,
useFactory: (api: WechatApiProvider, disabled: WechatDisabledProvider): IWechatProvider => {
useFactory: (
api: WechatApiProvider,
disabled: WechatDisabledProvider,
mock: WechatMockProvider,
): IWechatProvider => {
const cfg = loadAppConfig();
const enabled =
(cfg.wechatAuthEnabled || cfg.wechatPayEnabled) &&
(!!cfg.wxAppId || !!process.env.WX_MCH_ID);
return enabled ? api : disabled;
if (enabled) return api;
// preV1:真实微信未配置但开启 Mock 授权登录
if (cfg.mockWechat) return mock;
return disabled;
},
inject: [WechatApiProvider, WechatDisabledProvider],
inject: [WechatApiProvider, WechatDisabledProvider, WechatMockProvider],
},
PayMockProvider,
PayWechatProvider,
@@ -34,6 +34,10 @@ export class WechatApiProvider implements IWechatProvider {
return this.config.wechatAuthEnabled && !!this.appId && !!this.appSecret;
}
isMock() {
return false;
}
isPayEnabled() {
return (
this.config.wechatPayEnabled &&
@@ -7,6 +7,10 @@ export class WechatDisabledProvider implements IWechatProvider {
return false;
}
isMock() {
return false;
}
isPayEnabled() {
return false;
}
@@ -24,6 +24,9 @@ export type WechatPayNotifyResult = {
export interface IWechatProvider {
isEnabled(): boolean;
/** 是否为 preV1 Mock 实现(登录时可回落到演示账号) */
isMock(): boolean;
/** 微信支付是否已配置(商户号 + 证书) */
isPayEnabled(): boolean;
@@ -0,0 +1,76 @@
import { createHash } from 'crypto';
import { Injectable, NotImplementedException } from '@nestjs/common';
import type {
IWechatProvider,
WechatCodeSession,
WechatOAuthSession,
} from './wechat.interface';
/**
* preV1 Mock 微信 Provider。
*
* 目的:让「微信授权登录」按钮在不接真实微信的情况下走通。前端仍按真实 OAuth 流程
* (跳转 oauth-url → 回调携带 code),Mock 端将授权 URL 直接回跳并返回稳定 openId。
* 后续填入 WX_APP_ID/WX_APP_SECRET 并置 MOCK_WECHAT=false 即切换到真实实现。
*/
@Injectable()
export class WechatMockProvider implements IWechatProvider {
isEnabled() {
return true;
}
isMock() {
return true;
}
isPayEnabled() {
return false;
}
getMchId() {
return '';
}
/** 由 code 派生稳定 openId,保证同一 code 多次授权指向同一账号 */
private openIdFromCode(code: string): string {
return `mockwx_${createHash('md5').update(code).digest('hex').slice(0, 24)}`;
}
async code2Session(code: string): Promise<WechatCodeSession> {
return { openId: this.openIdFromCode(code), sessionKey: 'mock-session-key' };
}
async oauth2AccessToken(code: string): Promise<WechatOAuthSession> {
return { openId: this.openIdFromCode(code), accessToken: 'mock-access-token' };
}
async createJssdkConfig(url: string) {
return {
appId: 'mock-appid',
timestamp: Math.floor(Date.now() / 1000),
nonceStr: 'mocknonce',
signature: 'mocksignature',
url,
jsApiList: ['getLocation', 'scanQRCode', 'chooseImage'],
} as unknown as Awaited<ReturnType<IWechatProvider['createJssdkConfig']>>;
}
/** 直接把授权链接回跳到 redirectUri 并附带 mock code,模拟微信授权完成 */
buildOAuthUrl(redirectUri: string, state: string): string {
const sep = redirectUri.includes('?') ? '&' : '?';
const code = `mockcode_${state || 'default'}`;
return `${redirectUri}${sep}code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`;
}
async getPhoneNumberByCode(): Promise<string> {
throw new NotImplementedException('Mock 微信不支持获取手机号,请用短信绑定');
}
createJsapiPrepay(): never {
throw new NotImplementedException('FEATURE_DISABLED');
}
parsePayNotification(): never {
throw new NotImplementedException('FEATURE_DISABLED');
}
}