Compare commits
No commits in common. "533b2cd726d189616f534e4c1e8bac304cac7729" and "b8290d0cda34a4f7c2df489361d5618bf2fad71a" have entirely different histories.
533b2cd726
...
b8290d0cda
File diff suppressed because it is too large
Load Diff
2541
pnpm-lock.yaml
2541
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -32,7 +32,6 @@ import { CustomerTag } from '../entity/customer_tag.entity';
|
||||||
import { Customer } from '../entity/customer.entity';
|
import { Customer } from '../entity/customer.entity';
|
||||||
import { DeviceWhitelist } from '../entity/device_whitelist';
|
import { DeviceWhitelist } from '../entity/device_whitelist';
|
||||||
import { AuthCode } from '../entity/auth_code';
|
import { AuthCode } from '../entity/auth_code';
|
||||||
import { Subscription } from '../entity/subscription.entity';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// use for cookie sign key, should change to your own and keep security
|
// use for cookie sign key, should change to your own and keep security
|
||||||
|
|
@ -73,7 +72,6 @@ export default {
|
||||||
Customer,
|
Customer,
|
||||||
DeviceWhitelist,
|
DeviceWhitelist,
|
||||||
AuthCode,
|
AuthCode,
|
||||||
Subscription,
|
|
||||||
],
|
],
|
||||||
synchronize: true,
|
synchronize: true,
|
||||||
logging: false,
|
logging: false,
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
import { Controller, Inject, Param, Post, Get, Query } from '@midwayjs/core';
|
|
||||||
import { ApiOkResponse } from '@midwayjs/swagger';
|
|
||||||
import { SubscriptionService } from '../service/subscription.service';
|
|
||||||
import { errorResponse, successResponse } from '../utils/response.util';
|
|
||||||
import { BooleanRes, SubscriptionListRes } from '../dto/reponse.dto';
|
|
||||||
import { QuerySubscriptionDTO } from '../dto/subscription.dto';
|
|
||||||
|
|
||||||
@Controller('/subscription')
|
|
||||||
export class SubscriptionController {
|
|
||||||
@Inject()
|
|
||||||
subscriptionService: SubscriptionService;
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
|
||||||
@Post('/sync/:siteId')
|
|
||||||
async sync(@Param('siteId') siteId: string) {
|
|
||||||
try {
|
|
||||||
await this.subscriptionService.syncSubscriptions(siteId);
|
|
||||||
return successResponse(true);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '同步失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: SubscriptionListRes })
|
|
||||||
@Get('/list')
|
|
||||||
async list(@Query() query: QuerySubscriptionDTO) {
|
|
||||||
try {
|
|
||||||
const data = await this.subscriptionService.getSubscriptionList(query);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '获取失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -23,7 +23,6 @@ import { OrderNote } from '../entity/order_note.entity';
|
||||||
import { PaymentMethodDTO } from './logistics.dto';
|
import { PaymentMethodDTO } from './logistics.dto';
|
||||||
import { Flavors } from '../entity/flavors.entity';
|
import { Flavors } from '../entity/flavors.entity';
|
||||||
import { Strength } from '../entity/strength.entity';
|
import { Strength } from '../entity/strength.entity';
|
||||||
import { Subscription } from '../entity/subscription.entity';
|
|
||||||
|
|
||||||
export class BooleanRes extends SuccessWrapper(Boolean) {}
|
export class BooleanRes extends SuccessWrapper(Boolean) {}
|
||||||
//网站配置返回数据
|
//网站配置返回数据
|
||||||
|
|
@ -118,8 +117,3 @@ export class OrderDetailRes extends SuccessWrapper(OrderDetail) {}
|
||||||
export class PaymentMethodListRes extends SuccessArrayWrapper(
|
export class PaymentMethodListRes extends SuccessArrayWrapper(
|
||||||
PaymentMethodDTO
|
PaymentMethodDTO
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
// 订阅分页数据
|
|
||||||
export class SubscriptionPaginatedResponse extends PaginatedWrapper(Subscription) {}
|
|
||||||
// 订阅分页返回数据
|
|
||||||
export class SubscriptionListRes extends SuccessWrapper(SubscriptionPaginatedResponse) {}
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
|
||||||
import { SubscriptionStatus } from '../enums/base.enum';
|
|
||||||
|
|
||||||
export class QuerySubscriptionDTO {
|
|
||||||
@ApiProperty({ example: 1, description: '页码' })
|
|
||||||
@Rule(RuleType.number().default(1))
|
|
||||||
current: number;
|
|
||||||
|
|
||||||
@ApiProperty({ example: 10, description: '每页大小' })
|
|
||||||
@Rule(RuleType.number().default(10))
|
|
||||||
pageSize: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点ID' })
|
|
||||||
@Rule(RuleType.string().allow(''))
|
|
||||||
siteId: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '订阅状态', enum: SubscriptionStatus })
|
|
||||||
@Rule(RuleType.string().valid(...Object.values(SubscriptionStatus)).allow(''))
|
|
||||||
status: SubscriptionStatus | '';
|
|
||||||
|
|
||||||
@ApiProperty({ description: '客户邮箱' })
|
|
||||||
@Rule(RuleType.string().allow(''))
|
|
||||||
customer_email: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '关键字(订阅ID、邮箱等)' })
|
|
||||||
@Rule(RuleType.string().allow(''))
|
|
||||||
keyword: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Exclude, Expose } from 'class-transformer';
|
|
||||||
import {
|
|
||||||
Column,
|
|
||||||
CreateDateColumn,
|
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
UpdateDateColumn,
|
|
||||||
} from 'typeorm';
|
|
||||||
import { SubscriptionStatus } from '../enums/base.enum';
|
|
||||||
|
|
||||||
@Entity('subscription')
|
|
||||||
@Exclude()
|
|
||||||
export class Subscription {
|
|
||||||
@ApiProperty()
|
|
||||||
@PrimaryGeneratedColumn()
|
|
||||||
@Expose()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '来源站点唯一标识' })
|
|
||||||
@Column()
|
|
||||||
@Expose()
|
|
||||||
siteId: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'WooCommerce 订阅 ID' })
|
|
||||||
@Column()
|
|
||||||
@Expose()
|
|
||||||
externalSubscriptionId: string;
|
|
||||||
|
|
||||||
@ApiProperty({ type: SubscriptionStatus })
|
|
||||||
@Column({ type: 'enum', enum: SubscriptionStatus })
|
|
||||||
@Expose()
|
|
||||||
status: SubscriptionStatus;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ default: '' })
|
|
||||||
@Expose()
|
|
||||||
currency: string;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column('decimal', { precision: 10, scale: 2, default: 0 })
|
|
||||||
@Expose()
|
|
||||||
total: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '计费周期 e.g. day/week/month/year' })
|
|
||||||
@Column({ default: '' })
|
|
||||||
@Expose()
|
|
||||||
billing_period: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '计费周期间隔 e.g. 1/3/12' })
|
|
||||||
@Column({ type: 'int', default: 0 })
|
|
||||||
@Expose()
|
|
||||||
billing_interval: number;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ type: 'int', default: 0 })
|
|
||||||
@Expose()
|
|
||||||
customer_id: number;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ default: '' })
|
|
||||||
@Expose()
|
|
||||||
customer_email: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '父订单/父订阅ID(如有)' })
|
|
||||||
@Column({ type: 'int', default: 0 })
|
|
||||||
@Expose()
|
|
||||||
parent_id: number;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ type: 'timestamp', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
start_date: Date;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ type: 'timestamp', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
trial_end: Date;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ type: 'timestamp', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
next_payment_date: Date;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ type: 'timestamp', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
end_date: Date;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ type: 'json', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
line_items: any[];
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ type: 'json', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
meta_data: any[];
|
|
||||||
|
|
||||||
@ApiProperty({ example: '2022-12-12 11:11:11', description: '创建时间', required: true })
|
|
||||||
@CreateDateColumn()
|
|
||||||
@Expose()
|
|
||||||
createdAt: Date;
|
|
||||||
|
|
||||||
@ApiProperty({ example: '2022-12-12 11:11:11', description: '更新时间', required: true })
|
|
||||||
@UpdateDateColumn()
|
|
||||||
@Expose()
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
@ -72,14 +72,3 @@ export enum ShipmentType {
|
||||||
export enum staticValue {
|
export enum staticValue {
|
||||||
STATIC_CAPTCHA = 'yoone2025!@YOONE0923'
|
STATIC_CAPTCHA = 'yoone2025!@YOONE0923'
|
||||||
}
|
}
|
||||||
|
|
||||||
// WooCommerce Subscription status
|
|
||||||
// Reference: https://woocommerce.com/document/subscriptions/statuses/
|
|
||||||
export enum SubscriptionStatus {
|
|
||||||
ACTIVE = 'active', // 活跃
|
|
||||||
PENDING = 'pending', // 待处理/待激活
|
|
||||||
ON_HOLD = 'on-hold', // 暂停
|
|
||||||
CANCELLED = 'cancelled', // 已取消
|
|
||||||
EXPIRED = 'expired', // 已过期
|
|
||||||
PENDING_CANCELLATION = 'pending-cancel', // 待取消
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
import { Inject, Provide } from '@midwayjs/core';
|
|
||||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
|
||||||
import { Repository, Like } from 'typeorm';
|
|
||||||
import { WPService } from './wp.service';
|
|
||||||
import { Subscription } from '../entity/subscription.entity';
|
|
||||||
import { plainToClass } from 'class-transformer';
|
|
||||||
import { SubscriptionStatus } from '../enums/base.enum';
|
|
||||||
import { QuerySubscriptionDTO } from '../dto/subscription.dto';
|
|
||||||
|
|
||||||
@Provide()
|
|
||||||
export class SubscriptionService {
|
|
||||||
@Inject()
|
|
||||||
wPService: WPService;
|
|
||||||
|
|
||||||
@InjectEntityModel(Subscription)
|
|
||||||
subscriptionModel: Repository<Subscription>;
|
|
||||||
|
|
||||||
async syncSubscriptions(siteId: string) {
|
|
||||||
const subs = await this.wPService.getSubscriptions(siteId);
|
|
||||||
for (const sub of subs) {
|
|
||||||
await this.syncSingleSubscription(siteId, sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async syncSingleSubscription(siteId: string, sub: any) {
|
|
||||||
const { line_items, ...raw } = sub;
|
|
||||||
const entity: Partial<Subscription> = {
|
|
||||||
...raw,
|
|
||||||
externalSubscriptionId: String(raw.id),
|
|
||||||
siteId,
|
|
||||||
status: raw.status as SubscriptionStatus,
|
|
||||||
customer_email: raw?.billing?.email || raw?.customer_email || '',
|
|
||||||
line_items,
|
|
||||||
};
|
|
||||||
delete (entity as any).id;
|
|
||||||
const existing = await this.subscriptionModel.findOne({
|
|
||||||
where: { externalSubscriptionId: String(sub.id), siteId },
|
|
||||||
});
|
|
||||||
const saveEntity = plainToClass(Subscription, entity);
|
|
||||||
if (existing) {
|
|
||||||
await this.subscriptionModel.update({ id: existing.id }, saveEntity);
|
|
||||||
} else {
|
|
||||||
await this.subscriptionModel.save(saveEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getSubscriptionList({
|
|
||||||
current = 1,
|
|
||||||
pageSize = 10,
|
|
||||||
siteId,
|
|
||||||
status,
|
|
||||||
customer_email,
|
|
||||||
keyword,
|
|
||||||
}: QuerySubscriptionDTO) {
|
|
||||||
const where: any = {};
|
|
||||||
if (siteId) where.siteId = siteId;
|
|
||||||
if (status) where.status = status;
|
|
||||||
if (customer_email) where.customer_email = Like(`%${customer_email}%`);
|
|
||||||
if (keyword) {
|
|
||||||
where.externalSubscriptionId = Like(`%${keyword}%`);
|
|
||||||
}
|
|
||||||
const [list, total] = await this.subscriptionModel.findAndCount({
|
|
||||||
where,
|
|
||||||
order: { id: 'DESC' },
|
|
||||||
skip: (current - 1) * pageSize,
|
|
||||||
take: pageSize,
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
list,
|
|
||||||
total,
|
|
||||||
current,
|
|
||||||
pageSize,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -134,26 +134,6 @@ export class WPService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取 WooCommerce Subscriptions
|
|
||||||
* 优先尝试 wc/v1/subscriptions (Subscriptions 插件提供), 如失败则回退 wc/v3/subscriptions(部分版本提供)。
|
|
||||||
*/
|
|
||||||
async getSubscriptions(siteId: string): Promise<Record<string, any>[]> {
|
|
||||||
const site = this.geSite(siteId);
|
|
||||||
try {
|
|
||||||
return await this.fetchPagedData<Record<string, any>>(
|
|
||||||
'/wc/v1/subscriptions',
|
|
||||||
site
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
// fallback
|
|
||||||
return await this.fetchPagedData<Record<string, any>>(
|
|
||||||
'/wc/v3/subscriptions',
|
|
||||||
site
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getOrderRefund(
|
async getOrderRefund(
|
||||||
siteId: string,
|
siteId: string,
|
||||||
orderId: string,
|
orderId: string,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue