docs: 完善订阅相关模块的注释和文档

- 在 DTO、实体和服务中添加详细注释说明字段用途
- 补充订阅同步和列表查询的业务逻辑说明
- 明确分页返回数据的包装结构
This commit is contained in:
tikkhun 2025-11-14 16:26:26 +08:00
parent 533b2cd726
commit 2d36370acf
6 changed files with 44 additions and 3 deletions

View File

@ -10,6 +10,7 @@ export class SubscriptionController {
@Inject() @Inject()
subscriptionService: SubscriptionService; subscriptionService: SubscriptionService;
// 同步订阅:根据站点 ID 拉取并更新本地订阅数据
@ApiOkResponse({ type: BooleanRes }) @ApiOkResponse({ type: BooleanRes })
@Post('/sync/:siteId') @Post('/sync/:siteId')
async sync(@Param('siteId') siteId: string) { async sync(@Param('siteId') siteId: string) {
@ -21,6 +22,7 @@ export class SubscriptionController {
} }
} }
// 订阅列表:分页 + 筛选
@ApiOkResponse({ type: SubscriptionListRes }) @ApiOkResponse({ type: SubscriptionListRes })
@Get('/list') @Get('/list')
async list(@Query() query: QuerySubscriptionDTO) { async list(@Query() query: QuerySubscriptionDTO) {

View File

@ -119,7 +119,7 @@ export class PaymentMethodListRes extends SuccessArrayWrapper(
PaymentMethodDTO PaymentMethodDTO
) {} ) {}
// 订阅分页数据 // 订阅分页数据(列表 + 总数等分页信息)
export class SubscriptionPaginatedResponse extends PaginatedWrapper(Subscription) {} export class SubscriptionPaginatedResponse extends PaginatedWrapper(Subscription) {}
// 订阅分页返回数据 // 订阅分页返回数据(统一成功包装)
export class SubscriptionListRes extends SuccessWrapper(SubscriptionPaginatedResponse) {} export class SubscriptionListRes extends SuccessWrapper(SubscriptionPaginatedResponse) {}

View File

@ -2,27 +2,34 @@ import { ApiProperty } from '@midwayjs/swagger';
import { Rule, RuleType } from '@midwayjs/validate'; import { Rule, RuleType } from '@midwayjs/validate';
import { SubscriptionStatus } from '../enums/base.enum'; import { SubscriptionStatus } from '../enums/base.enum';
// 订阅列表查询参数(分页与筛选)
export class QuerySubscriptionDTO { export class QuerySubscriptionDTO {
// 当前页码(从 1 开始)
@ApiProperty({ example: 1, description: '页码' }) @ApiProperty({ example: 1, description: '页码' })
@Rule(RuleType.number().default(1)) @Rule(RuleType.number().default(1))
current: number; current: number;
// 每页数量
@ApiProperty({ example: 10, description: '每页大小' }) @ApiProperty({ example: 10, description: '每页大小' })
@Rule(RuleType.number().default(10)) @Rule(RuleType.number().default(10))
pageSize: number; pageSize: number;
// 站点 ID可选
@ApiProperty({ description: '站点ID' }) @ApiProperty({ description: '站点ID' })
@Rule(RuleType.string().allow('')) @Rule(RuleType.string().allow(''))
siteId: string; siteId: string;
// 订阅状态筛选(可选),支持枚举值
@ApiProperty({ description: '订阅状态', enum: SubscriptionStatus }) @ApiProperty({ description: '订阅状态', enum: SubscriptionStatus })
@Rule(RuleType.string().valid(...Object.values(SubscriptionStatus)).allow('')) @Rule(RuleType.string().valid(...Object.values(SubscriptionStatus)).allow(''))
status: SubscriptionStatus | ''; status: SubscriptionStatus | '';
// 客户邮箱(模糊匹配,可选)
@ApiProperty({ description: '客户邮箱' }) @ApiProperty({ description: '客户邮箱' })
@Rule(RuleType.string().allow('')) @Rule(RuleType.string().allow(''))
customer_email: string; customer_email: string;
// 关键字订阅ID、邮箱等模糊匹配可选
@ApiProperty({ description: '关键字订阅ID、邮箱等' }) @ApiProperty({ description: '关键字订阅ID、邮箱等' })
@Rule(RuleType.string().allow('')) @Rule(RuleType.string().allow(''))
keyword: string; keyword: string;

View File

@ -12,96 +12,115 @@ import { SubscriptionStatus } from '../enums/base.enum';
@Entity('subscription') @Entity('subscription')
@Exclude() @Exclude()
export class Subscription { export class Subscription {
// 本地主键,自增 ID
@ApiProperty() @ApiProperty()
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
@Expose() @Expose()
id: number; id: number;
// 站点唯一标识,用于区分不同来源站点
@ApiProperty({ description: '来源站点唯一标识' }) @ApiProperty({ description: '来源站点唯一标识' })
@Column() @Column()
@Expose() @Expose()
siteId: string; siteId: string;
// WooCommerce 订阅的原始 ID字符串化用于幂等更新
@ApiProperty({ description: 'WooCommerce 订阅 ID' }) @ApiProperty({ description: 'WooCommerce 订阅 ID' })
@Column() @Column()
@Expose() @Expose()
externalSubscriptionId: string; externalSubscriptionId: string;
// 订阅状态active/cancelled/on-hold 等)
@ApiProperty({ type: SubscriptionStatus }) @ApiProperty({ type: SubscriptionStatus })
@Column({ type: 'enum', enum: SubscriptionStatus }) @Column({ type: 'enum', enum: SubscriptionStatus })
@Expose() @Expose()
status: SubscriptionStatus; status: SubscriptionStatus;
// 货币代码,例如 USD/CAD
@ApiProperty() @ApiProperty()
@Column({ default: '' }) @Column({ default: '' })
@Expose() @Expose()
currency: string; currency: string;
// 总金额,保留两位小数
@ApiProperty() @ApiProperty()
@Column('decimal', { precision: 10, scale: 2, default: 0 }) @Column('decimal', { precision: 10, scale: 2, default: 0 })
@Expose() @Expose()
total: number; total: number;
// 计费周期day/week/month/year
@ApiProperty({ description: '计费周期 e.g. day/week/month/year' }) @ApiProperty({ description: '计费周期 e.g. day/week/month/year' })
@Column({ default: '' }) @Column({ default: '' })
@Expose() @Expose()
billing_period: string; billing_period: string;
// 计费周期间隔(例如 1/3/12
@ApiProperty({ description: '计费周期间隔 e.g. 1/3/12' }) @ApiProperty({ description: '计费周期间隔 e.g. 1/3/12' })
@Column({ type: 'int', default: 0 }) @Column({ type: 'int', default: 0 })
@Expose() @Expose()
billing_interval: number; billing_interval: number;
// 客户 IDWooCommerce 用户 ID
@ApiProperty() @ApiProperty()
@Column({ type: 'int', default: 0 }) @Column({ type: 'int', default: 0 })
@Expose() @Expose()
customer_id: number; customer_id: number;
// 客户邮箱(从 billing.email 或 customer_email 提取)
@ApiProperty() @ApiProperty()
@Column({ default: '' }) @Column({ default: '' })
@Expose() @Expose()
customer_email: string; customer_email: string;
// 父订单/订阅 ID如有
@ApiProperty({ description: '父订单/父订阅ID如有' }) @ApiProperty({ description: '父订单/父订阅ID如有' })
@Column({ type: 'int', default: 0 }) @Column({ type: 'int', default: 0 })
@Expose() @Expose()
parent_id: number; parent_id: number;
// 订阅开始时间
@ApiProperty() @ApiProperty()
@Column({ type: 'timestamp', nullable: true }) @Column({ type: 'timestamp', nullable: true })
@Expose() @Expose()
start_date: Date; start_date: Date;
// 试用结束时间
@ApiProperty() @ApiProperty()
@Column({ type: 'timestamp', nullable: true }) @Column({ type: 'timestamp', nullable: true })
@Expose() @Expose()
trial_end: Date; trial_end: Date;
// 下次支付时间
@ApiProperty() @ApiProperty()
@Column({ type: 'timestamp', nullable: true }) @Column({ type: 'timestamp', nullable: true })
@Expose() @Expose()
next_payment_date: Date; next_payment_date: Date;
// 订阅结束时间
@ApiProperty() @ApiProperty()
@Column({ type: 'timestamp', nullable: true }) @Column({ type: 'timestamp', nullable: true })
@Expose() @Expose()
end_date: Date; end_date: Date;
// 商品项(订阅行项目)
@ApiProperty() @ApiProperty()
@Column({ type: 'json', nullable: true }) @Column({ type: 'json', nullable: true })
@Expose() @Expose()
line_items: any[]; line_items: any[];
// 额外元数据(键值对)
@ApiProperty() @ApiProperty()
@Column({ type: 'json', nullable: true }) @Column({ type: 'json', nullable: true })
@Expose() @Expose()
meta_data: any[]; meta_data: any[];
// 创建时间(数据库自动生成)
@ApiProperty({ example: '2022-12-12 11:11:11', description: '创建时间', required: true }) @ApiProperty({ example: '2022-12-12 11:11:11', description: '创建时间', required: true })
@CreateDateColumn() @CreateDateColumn()
@Expose() @Expose()
createdAt: Date; createdAt: Date;
// 更新时间(数据库自动生成)
@ApiProperty({ example: '2022-12-12 11:11:11', description: '更新时间', required: true }) @ApiProperty({ example: '2022-12-12 11:11:11', description: '更新时间', required: true })
@UpdateDateColumn() @UpdateDateColumn()
@Expose() @Expose()

View File

@ -15,6 +15,10 @@ export class SubscriptionService {
@InjectEntityModel(Subscription) @InjectEntityModel(Subscription)
subscriptionModel: Repository<Subscription>; subscriptionModel: Repository<Subscription>;
/**
*
* - WooCommerce /
*/
async syncSubscriptions(siteId: string) { async syncSubscriptions(siteId: string) {
const subs = await this.wPService.getSubscriptions(siteId); const subs = await this.wPService.getSubscriptions(siteId);
for (const sub of subs) { for (const sub of subs) {
@ -22,6 +26,11 @@ export class SubscriptionService {
} }
} }
/**
*
* - externalSubscriptionId
* -
*/
async syncSingleSubscription(siteId: string, sub: any) { async syncSingleSubscription(siteId: string, sub: any) {
const { line_items, ...raw } = sub; const { line_items, ...raw } = sub;
const entity: Partial<Subscription> = { const entity: Partial<Subscription> = {
@ -44,6 +53,9 @@ export class SubscriptionService {
} }
} }
/**
*
*/
async getSubscriptionList({ async getSubscriptionList({
current = 1, current = 1,
pageSize = 10, pageSize = 10,

View File

@ -136,7 +136,8 @@ export class WPService {
/** /**
* WooCommerce Subscriptions * WooCommerce Subscriptions
* wc/v1/subscriptions (Subscriptions ), 退 wc/v3/subscriptions * wc/v1/subscriptionsSubscriptions 退 wc/v3/subscriptions
*
*/ */
async getSubscriptions(siteId: string): Promise<Record<string, any>[]> { async getSubscriptions(siteId: string): Promise<Record<string, any>[]> {
const site = this.geSite(siteId); const site = this.geSite(siteId);