feat: implement demo candidate 1.0.0-rc.1

This commit is contained in:
2026-09-03 21:48:12 +08:00
commit 043f8a823c
25 changed files with 9883 additions and 0 deletions

118
src/service/goods.ts Normal file
View File

@@ -0,0 +1,118 @@
import { Inject, Provide } from '@midwayjs/core';
import {
InjectSaasCrud,
SaasAction,
SaasActionContext,
SaasBusinessReject,
SaasCrudCaller,
SaasCrudService,
SaasCrudServiceBase,
} from '@cool-midway/module-runtime';
import {
Actions,
Crud,
Events,
type GoodsBatchInfoInput,
type GoodsBatchInfoOutput,
type GoodsInfoOutput,
type GoodsReleaseStockInput,
type GoodsReleaseStockOutput,
type GoodsReserveStockInput,
type GoodsReserveStockOutput,
} from '../saas/generated';
@Provide()
@SaasCrudService(Crud.Goods)
export class GoodsService extends SaasCrudServiceBase<typeof Crud.Goods> {
@InjectSaasCrud(Crud.Goods)
private readonly goods!: SaasCrudCaller<typeof Crud.Goods>;
@Inject()
private readonly saasActionContext!: SaasActionContext;
@SaasAction(Actions.GoodsBatchInfo)
async batchInfo(
input: GoodsBatchInfoInput
): Promise<GoodsBatchInfoOutput> {
const list: GoodsInfoOutput[] = [];
for (const id of input.ids) {
const goods = await this.findGoods(id);
if (goods) list.push(goods);
}
return { list };
}
@SaasAction(Actions.GoodsReserveStock)
async reserveStock(
input: GoodsReserveStockInput
): Promise<GoodsReserveStockOutput> {
const goods = await this.requireGoods(input.goodsId);
if (goods.status !== 1) {
throw new SaasBusinessReject(
'GOODS_UNAVAILABLE',
`商品 ${input.goodsId} 当前不可售`
);
}
if (goods.stock < input.quantity) {
throw new SaasBusinessReject(
'INSUFFICIENT_STOCK',
`商品 ${input.goodsId} 库存不足`,
{ available: goods.stock, requested: input.quantity }
);
}
const remainingStock = goods.stock - input.quantity;
this.saasActionContext.raise(Events.DemoGoodsStockReserved, {
goodsId: input.goodsId,
quantity: input.quantity,
remainingStock,
});
await this.updateStock(input.goodsId, remainingStock);
return { goodsId: input.goodsId, remainingStock };
}
@SaasAction(Actions.GoodsReleaseStock)
async releaseStock(
input: GoodsReleaseStockInput
): Promise<GoodsReleaseStockOutput> {
const goods = await this.requireGoods(input.goodsId);
const currentStock = goods.stock + input.quantity;
this.saasActionContext.raise(Events.DemoGoodsStockReserved, {
goodsId: input.goodsId,
quantity: input.quantity,
remainingStock: currentStock,
});
await this.updateStock(input.goodsId, currentStock);
return { goodsId: input.goodsId, currentStock };
}
private async findGoods(goodsId: number): Promise<GoodsInfoOutput | null> {
const rows = await this.goods.list({
where: { id: String(goodsId) },
take: 1,
});
return rows[0] || null;
}
private async requireGoods(goodsId: number): Promise<GoodsInfoOutput> {
const goods = await this.findGoods(goodsId);
if (!goods) {
throw new SaasBusinessReject(
'GOODS_NOT_FOUND',
`商品 ${goodsId} 不存在`
);
}
return goods;
}
private async updateStock(goodsId: number, stock: number): Promise<void> {
const result = await this.goods.update({ id: String(goodsId), stock });
if (result.affected !== 1) {
throw new SaasBusinessReject(
'GOODS_STOCK_UPDATE_FAILED',
`商品 ${goodsId} 库存更新失败`
);
}
}
}