14
packages/shared/src/contract.ts
Normal file
14
packages/shared/src/contract.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { CardTypeCategory } from './enums'
|
||||
|
||||
/** 月卡与多次卡属于预付式服务,必须签署书面合同;单次卡和体验卡不签。 */
|
||||
export function requiresMembershipContract(card: { type: CardTypeCategory | string; totalTimes: number | null }): boolean {
|
||||
if (card.type === CardTypeCategory.DURATION) return true
|
||||
if (card.type === CardTypeCategory.TIMES) return card.totalTimes === null || card.totalTimes > 1
|
||||
return false
|
||||
}
|
||||
|
||||
/** 预约接口返回此文案时,前端引导学员去签署合同。 */
|
||||
export const CONTRACT_UNSIGNED_MESSAGE = '请先签署会员卡合同后再预约'
|
||||
|
||||
export const CONTRACT_REAL_NAME_MAX_LENGTH = 30
|
||||
export const CONTRACT_SIGNATURE_MAX_BYTES = 300 * 1024
|
||||
@@ -47,6 +47,18 @@ export enum OrderStatus {
|
||||
REFUNDED = 'REFUNDED',
|
||||
}
|
||||
|
||||
// ===== Membership Contract =====
|
||||
export enum MembershipContractStatus {
|
||||
PENDING_SIGN = 'PENDING_SIGN',
|
||||
SIGNED = 'SIGNED',
|
||||
VOIDED = 'VOIDED',
|
||||
}
|
||||
|
||||
export enum MembershipContractSource {
|
||||
PURCHASE = 'PURCHASE', // 支付后签署
|
||||
LEGACY = 'LEGACY', // 存量会员卡补签
|
||||
}
|
||||
|
||||
// ===== Invite =====
|
||||
export enum InviteReferralStatus {
|
||||
REGISTERED = 'REGISTERED',
|
||||
|
||||
@@ -28,6 +28,8 @@ export {
|
||||
ProfessionalAssessmentKind,
|
||||
TrainingPlanStatus,
|
||||
ProgressPhotoAngle,
|
||||
MembershipContractStatus,
|
||||
MembershipContractSource,
|
||||
} from './enums'
|
||||
|
||||
// Constants
|
||||
@@ -150,3 +152,6 @@ export * from './types/body-portrait'
|
||||
export * from './types/shop'
|
||||
|
||||
export * from './types/teaching-case'
|
||||
|
||||
export * from './contract'
|
||||
export * from './types/contract'
|
||||
|
||||
145
packages/shared/src/types/contract.ts
Normal file
145
packages/shared/src/types/contract.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import type { MembershipContractSource, MembershipContractStatus } from '../enums'
|
||||
|
||||
export type ContractKind = 'MONTHLY' | 'MULTI_TIMES'
|
||||
export type ContractPremisesType = 'LEASED' | 'OWNED'
|
||||
|
||||
export interface ContractRow {
|
||||
readonly label: string
|
||||
readonly value: string
|
||||
}
|
||||
|
||||
export interface ContractClause {
|
||||
readonly text: string
|
||||
/** 与学员有重大利害关系的条款,需加粗提示。 */
|
||||
readonly emphasis?: boolean
|
||||
}
|
||||
|
||||
export interface ContractSection {
|
||||
readonly title: string
|
||||
readonly clauses: readonly ContractClause[]
|
||||
}
|
||||
|
||||
/** 渲染后的合同正文;签署时冻结并计算 SHA-256。 */
|
||||
export interface ContractDocument {
|
||||
readonly title: string
|
||||
readonly contractNo: string
|
||||
readonly templateVersion: string
|
||||
readonly notice: string
|
||||
readonly partyA: readonly ContractRow[]
|
||||
readonly partyB: readonly ContractRow[]
|
||||
readonly cardRows: readonly ContractRow[]
|
||||
readonly sections: readonly ContractSection[]
|
||||
readonly commitment: string
|
||||
}
|
||||
|
||||
export interface ContractSignatureInfo {
|
||||
readonly signerName: string
|
||||
readonly signedAt: string
|
||||
/** data:image/png;base64,... */
|
||||
readonly memberSignature: string
|
||||
readonly partyBSignature: string | null
|
||||
readonly partyBName: string
|
||||
readonly contentSha256: string
|
||||
}
|
||||
|
||||
export interface MembershipContractSummary {
|
||||
readonly id: string
|
||||
readonly contractNo: string
|
||||
readonly status: MembershipContractStatus
|
||||
readonly source: MembershipContractSource
|
||||
readonly requireBeforeBooking: boolean
|
||||
readonly cardName: string
|
||||
readonly kind: ContractKind
|
||||
readonly amount: number
|
||||
readonly startDate: string
|
||||
readonly expireDate: string
|
||||
readonly createdAt: string
|
||||
readonly signedAt: string | null
|
||||
readonly voidReason: string | null
|
||||
}
|
||||
|
||||
export interface MembershipContractDetail extends MembershipContractSummary {
|
||||
readonly partyName: string | null
|
||||
readonly partyPhone: string | null
|
||||
readonly document: ContractDocument
|
||||
readonly contentSha256: string
|
||||
readonly signature: ContractSignatureInfo | null
|
||||
/** 乙方信息未配置完成时不能签署。 */
|
||||
readonly signable: boolean
|
||||
readonly unsignableReason: string | null
|
||||
}
|
||||
|
||||
export interface AdminMembershipContractRow extends MembershipContractSummary {
|
||||
readonly user: { readonly id: string; readonly nickname: string; readonly phone: string | null }
|
||||
readonly partyName: string | null
|
||||
}
|
||||
|
||||
export interface ContractPendingSummary {
|
||||
readonly pendingCount: number
|
||||
readonly blockingCount: number
|
||||
readonly firstPendingId: string | null
|
||||
}
|
||||
|
||||
export interface SaveContractPartyADto {
|
||||
readonly realName: string
|
||||
readonly phone: string
|
||||
}
|
||||
|
||||
export interface SignMembershipContractDto {
|
||||
readonly contentSha256: string
|
||||
readonly signatureImage: string
|
||||
readonly agreedTerms: boolean
|
||||
readonly confirmedHealth: boolean
|
||||
readonly device?: string
|
||||
}
|
||||
|
||||
export interface ContractPartyConfig {
|
||||
readonly businessName: string
|
||||
readonly brandName: string
|
||||
readonly creditCode: string
|
||||
readonly operatorName: string
|
||||
readonly phone: string
|
||||
readonly address: string
|
||||
readonly premisesType: ContractPremisesType
|
||||
readonly leaseEndDate: string | null
|
||||
/** 单节标准课价(分),用于学员原因退费结算;未配置时取单次卡售价。 */
|
||||
readonly standardLessonPrice: number
|
||||
readonly standardLessonPriceConfigured: boolean
|
||||
readonly signatureImage: string | null
|
||||
readonly signatureUpdatedAt: string | null
|
||||
readonly missingFields: readonly string[]
|
||||
}
|
||||
|
||||
export interface UpdateContractPartyDto {
|
||||
readonly businessName?: string
|
||||
readonly brandName?: string
|
||||
readonly creditCode?: string
|
||||
readonly operatorName?: string
|
||||
readonly phone?: string
|
||||
readonly address?: string
|
||||
readonly premisesType?: ContractPremisesType
|
||||
readonly leaseEndDate?: string | null
|
||||
readonly standardLessonPrice?: number
|
||||
}
|
||||
|
||||
export interface LegacyContractCandidate {
|
||||
readonly membershipId: string
|
||||
readonly user: { readonly id: string; readonly nickname: string; readonly phone: string | null }
|
||||
readonly cardName: string
|
||||
readonly kind: ContractKind
|
||||
readonly remainingTimes: number | null
|
||||
readonly totalTimes: number | null
|
||||
readonly startDate: string
|
||||
readonly expireDate: string
|
||||
}
|
||||
|
||||
export interface LegacyContractSyncResult {
|
||||
readonly created: number
|
||||
readonly needsAmount: readonly LegacyContractCandidate[]
|
||||
}
|
||||
|
||||
export interface CreateLegacyContractDto {
|
||||
readonly membershipId: string
|
||||
/** 实付金额(分) */
|
||||
readonly amount: number
|
||||
}
|
||||
Reference in New Issue
Block a user