9 Commits

10 changed files with 57 additions and 360 deletions

View File

@@ -1,8 +1,8 @@
{ {
"moduleCode": "demo", "moduleCode": "demo",
"candidateVersion": "1.0.10-rc.10", "candidateVersion": "1.0.0-rc.1",
"buildRevision": 10, "buildRevision": 1,
"buildChecksum": "e4556add96ae3edd54c453115fb677f52d7f9eccb2232093e13f78fb92bed762", "buildChecksum": "07cb8511791fc96f65d6353e47a1744e448158f2f766a4110d539806801c596c",
"schemaInputChecksum": "5a47ef42d5af4736ae307150625beb1cea177c191c30864747f3a4a66f9aad23", "schemaInputChecksum": "8a55cecb643f802ef0f39ade2f31fe8313ad437d72d12b64525c0b1a6892813d",
"branchName": "release/1.0.10-rc.10" "branchName": "release/1.0.0-rc.1"
} }

5
bootstrap.js vendored
View File

@@ -11,11 +11,10 @@ function loadGeneratedSaasModule() {
return require('./dist/saas/generated').GeneratedSaasModule; return require('./dist/saas/generated').GeneratedSaasModule;
} }
async function start(options = {}) { async function start() {
const session = await prepareSaasServiceHost({ await prepareSaasServiceHost({
generated: loadGeneratedSaasModule(), generated: loadGeneratedSaasModule(),
}); });
if (options.verifySession) await options.verifySession(session);
requireExactReleaseEnvironment(); requireExactReleaseEnvironment();
return Bootstrap.configure({ return Bootstrap.configure({
baseDir: path.join(__dirname, 'dist'), baseDir: path.join(__dirname, 'dist'),

View File

@@ -10,19 +10,17 @@
"build": "rimraf dist && mwtsc --cleanOutDir", "build": "rimraf dist && mwtsc --cleanOutDir",
"publish": "cool saas-model finalize", "publish": "cool saas-model finalize",
"start": "node bootstrap.js", "start": "node bootstrap.js",
"start:stable": "node start-stable.js",
"test:stable": "node --test test/start-stable.test.cjs",
"test:business": "node --test test/goods-business-input.test.cjs",
"start:local": "cross-env NODE_ENV=local SAAS_RUNTIME_BOOTSTRAP_LOCAL_FALLBACK_ENABLED=true node bootstrap.js", "start:local": "cross-env NODE_ENV=local SAAS_RUNTIME_BOOTSTRAP_LOCAL_FALLBACK_ENABLED=true node bootstrap.js",
"verify:packages": "node scripts/verify-local-packages.cjs", "verify:packages": "node scripts/verify-local-packages.cjs",
"start:frozen": "node start-frozen.js" "start:frozen": "node start-frozen.js",
"start:stable": "node start-stable.js",
"test:stable": "node --test test/start-stable.test.cjs"
}, },
"coolSaas": { "coolSaas": {
"lifecycle": { "lifecycle": {
"dev": "pnpm run build && pnpm run start:local", "dev": "pnpm run build && pnpm run start:local",
"test": "pnpm run typecheck && pnpm run test:stable && pnpm run test:business", "test": "pnpm run typecheck",
"build": "pnpm run build", "build": "pnpm run build"
"artifactDir": "dist"
} }
}, },
"dependencies": { "dependencies": {

View File

@@ -8,7 +8,6 @@ import {
import type { SaasCrudAfterChange, SaasCrudBeforeChange, SaasEventEnvelope } from '@cool-midway/module-runtime'; import type { SaasCrudAfterChange, SaasCrudBeforeChange, SaasEventEnvelope } from '@cool-midway/module-runtime';
export type GoodsAddInput = { export type GoodsAddInput = {
description?: string | null;
price: string; price: string;
status?: number; status?: number;
stock?: number; stock?: number;
@@ -16,7 +15,6 @@ export type GoodsAddInput = {
}; };
export type GoodsAddOutput = { export type GoodsAddOutput = {
description?: string | null;
id: string; id: string;
price: string; price: string;
status: number; status: number;
@@ -45,7 +43,6 @@ export type GoodsInfoInput = {
}; };
export type GoodsInfoOutput = { export type GoodsInfoOutput = {
description?: string | null;
id: string; id: string;
price: string; price: string;
status: number; status: number;
@@ -69,14 +66,13 @@ export type GoodsListInput = {
}; };
}; };
export type GoodsListOutput = ({ export type GoodsListOutput = {
description?: string | null;
id: string; id: string;
price: string; price: string;
status: number; status: number;
stock: number; stock: number;
title: string; title: string;
})[]; }[];
export type GoodsPageInput = { export type GoodsPageInput = {
keyword?: string; keyword?: string;
@@ -96,14 +92,13 @@ export type GoodsPageInput = {
}; };
export type GoodsPageOutput = { export type GoodsPageOutput = {
list: ({ list: {
description?: string | null;
id: string; id: string;
price: string; price: string;
status: number; status: number;
stock: number; stock: number;
title: string; title: string;
})[]; }[];
pagination: { pagination: {
page: number; page: number;
size: number; size: number;
@@ -133,7 +128,6 @@ export type GoodsReserveStockOutput = {
}; };
export type GoodsUpdateInput = { export type GoodsUpdateInput = {
description?: string | null;
id: string; id: string;
price?: string; price?: string;
status?: number; status?: number;
@@ -198,13 +192,6 @@ export const Actions = {
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -212,7 +199,7 @@ export const Actions = {
"price" "price"
] ]
}, },
"inputChecksum": "1631b9abe9afce3e828787e355f4c380fc3d6210f58260797109014763bddab7", "inputChecksum": "c2984884419729416e6cd81e4cddc6d82486b6dde2d9de0ca17533f69f55816a",
"outputSchema": { "outputSchema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -234,13 +221,6 @@ export const Actions = {
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -251,7 +231,7 @@ export const Actions = {
"status" "status"
] ]
}, },
"outputChecksum": "744f5d2ad90a1c4e21a1ff66eaae83a3f900cdd685d6ee409f03555336c9a067", "outputChecksum": "bb691ac7eaa19bb231f9da14a80848262965255fb37726ab155fb9804c9a55b1",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:add" "permission": "demo:goods:add"
@@ -260,7 +240,6 @@ export const Actions = {
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -477,13 +456,6 @@ export const Actions = {
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -494,7 +466,7 @@ export const Actions = {
"status" "status"
] ]
}, },
"outputChecksum": "744f5d2ad90a1c4e21a1ff66eaae83a3f900cdd685d6ee409f03555336c9a067", "outputChecksum": "bb691ac7eaa19bb231f9da14a80848262965255fb37726ab155fb9804c9a55b1",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:info" "permission": "demo:goods:info"
@@ -503,7 +475,6 @@ export const Actions = {
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -616,13 +587,6 @@ export const Actions = {
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -634,7 +598,7 @@ export const Actions = {
] ]
} }
}, },
"outputChecksum": "909ff0a80769ea17a9a6159097c45c5097286e53f5010a514491df4d1f177518", "outputChecksum": "2182ab26245d80a9a40216cce7cadbe8217ea12813f1cf536b7a59ba0a4ed4ae",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:list" "permission": "demo:goods:list"
@@ -643,7 +607,6 @@ export const Actions = {
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -764,13 +727,6 @@ export const Actions = {
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -812,7 +768,7 @@ export const Actions = {
"pagination" "pagination"
] ]
}, },
"outputChecksum": "476124b6a81b38277a80b1f773dffad01acee546c6140689abea6532d4216d2f", "outputChecksum": "15ee81575d900276aaa2437afa5ee6f41321e5b00ab58706322e5d5c0809bf8d",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:page" "permission": "demo:goods:page"
@@ -821,7 +777,6 @@ export const Actions = {
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -1009,13 +964,6 @@ export const Actions = {
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -1023,7 +971,7 @@ export const Actions = {
], ],
"minProperties": 2 "minProperties": 2
}, },
"inputChecksum": "103a179289c7ad50a87b38efeb58b281ee2cc03c3dabbe20814e26f2b804cee8", "inputChecksum": "ced49f501c83dd1dce9725c913a1761566828004c851779a5db85ddd2bb9123c",
"outputSchema": { "outputSchema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -1046,7 +994,6 @@ export const Actions = {
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -1097,7 +1044,7 @@ export const Crud = {
moduleCode: "demo", moduleCode: "demo",
tableCode: "goods", tableCode: "goods",
operations: ["add","delete","update","info","list","page"], operations: ["add","delete","update","info","list","page"],
descriptorChecksum: "57f429f54ea377c92d648e4ffcc92611cbc288f227790cc1ff386f083bb7ce29", descriptorChecksum: "22211efee07c5c1aada51d508d9c053e0d7388ad6fc0da12c95a4d1de2e3ca53",
actions: { actions: {
add: Actions.GoodsAdd, add: Actions.GoodsAdd,
delete: Actions.GoodsDelete, delete: Actions.GoodsDelete,

View File

@@ -12,16 +12,15 @@ export interface GoodsRow extends Record<string, unknown> {
price: string; price: string;
stock: number; stock: number;
status: number; status: number;
description?: string | null;
} }
export class Goods extends SaasTableModel<GoodsRow> { export class Goods extends SaasTableModel<GoodsRow> {
static readonly definition = defineSaasTableModel({ static readonly definition = defineSaasTableModel({
"kind": "table", "kind": "table",
"moduleCode": "demo", "moduleCode": "demo",
"revision": 10, "revision": 1,
"schemaVersion": 10, "schemaVersion": 1,
"schemaInputChecksum": "5a47ef42d5af4736ae307150625beb1cea177c191c30864747f3a4a66f9aad23", "schemaInputChecksum": "8a55cecb643f802ef0f39ade2f31fe8313ad437d72d12b64525c0b1a6892813d",
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
{ {
@@ -73,16 +72,6 @@ export class Goods extends SaasTableModel<GoodsRow> {
"writable": true, "writable": true,
"filterable": true, "filterable": true,
"sortable": true "sortable": true
},
{
"code": "description",
"type": "varchar",
"nullable": true,
"primary": false,
"readable": true,
"writable": true,
"filterable": false,
"sortable": false
} }
] ]
}); });

View File

@@ -4,9 +4,9 @@ import { defineGeneratedSaasModule } from '@cool-midway/module-runtime';
export const GeneratedSaasModule = defineGeneratedSaasModule({ export const GeneratedSaasModule = defineGeneratedSaasModule({
"formatVersion": 2, "formatVersion": 2,
"moduleCode": "demo", "moduleCode": "demo",
"revision": 10, "revision": 1,
"schemaVersion": 10, "schemaVersion": 1,
"schemaInputChecksum": "5a47ef42d5af4736ae307150625beb1cea177c191c30864747f3a4a66f9aad23", "schemaInputChecksum": "8a55cecb643f802ef0f39ade2f31fe8313ad437d72d12b64525c0b1a6892813d",
"runtimeContractChecksum": "9ec90874dd2f0087423106ecffa0c17c9aa4362c597eca9eef9f25326b556c1e", "runtimeContractChecksum": "c1327810dd08dfd0cc1afeee99d7e9707fe14595272a3aa92ee13fc1d54aa0e5",
"implementationManifestChecksum": "d18964a3d40e3c9a65d83996ca3141a56043986c395f5d4cac57c60aa72f4236" "implementationManifestChecksum": "d18964a3d40e3c9a65d83996ca3141a56043986c395f5d4cac57c60aa72f4236"
}); });

View File

@@ -1,11 +1,11 @@
{ {
"manifestVersion": 1, "manifestVersion": 1,
"moduleCode": "demo", "moduleCode": "demo",
"revision": 10, "revision": 1,
"schemaChecksum": null, "schemaChecksum": null,
"schemaInputChecksum": "5a47ef42d5af4736ae307150625beb1cea177c191c30864747f3a4a66f9aad23", "schemaInputChecksum": "8a55cecb643f802ef0f39ade2f31fe8313ad437d72d12b64525c0b1a6892813d",
"actionManifestChecksum": null, "actionManifestChecksum": null,
"runtimeContractChecksum": "9ec90874dd2f0087423106ecffa0c17c9aa4362c597eca9eef9f25326b556c1e", "runtimeContractChecksum": "c1327810dd08dfd0cc1afeee99d7e9707fe14595272a3aa92ee13fc1d54aa0e5",
"implementationManifestChecksum": "d18964a3d40e3c9a65d83996ca3141a56043986c395f5d4cac57c60aa72f4236", "implementationManifestChecksum": "d18964a3d40e3c9a65d83996ca3141a56043986c395f5d4cac57c60aa72f4236",
"actions": [ "actions": [
"GoodsAdd", "GoodsAdd",
@@ -50,7 +50,7 @@
"list", "list",
"page" "page"
], ],
"descriptorChecksum": "57f429f54ea377c92d648e4ffcc92611cbc288f227790cc1ff386f083bb7ce29", "descriptorChecksum": "22211efee07c5c1aada51d508d9c053e0d7388ad6fc0da12c95a4d1de2e3ca53",
"actions": { "actions": {
"add": "GoodsAdd", "add": "GoodsAdd",
"delete": "GoodsDelete", "delete": "GoodsDelete",
@@ -86,13 +86,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -100,7 +93,7 @@
"price" "price"
] ]
}, },
"inputChecksum": "1631b9abe9afce3e828787e355f4c380fc3d6210f58260797109014763bddab7", "inputChecksum": "c2984884419729416e6cd81e4cddc6d82486b6dde2d9de0ca17533f69f55816a",
"outputSchema": { "outputSchema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -122,13 +115,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -139,7 +125,7 @@
"status" "status"
] ]
}, },
"outputChecksum": "744f5d2ad90a1c4e21a1ff66eaae83a3f900cdd685d6ee409f03555336c9a067", "outputChecksum": "bb691ac7eaa19bb231f9da14a80848262965255fb37726ab155fb9804c9a55b1",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:add" "permission": "demo:goods:add"
@@ -148,7 +134,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -358,13 +343,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -375,7 +353,7 @@
"status" "status"
] ]
}, },
"outputChecksum": "744f5d2ad90a1c4e21a1ff66eaae83a3f900cdd685d6ee409f03555336c9a067", "outputChecksum": "bb691ac7eaa19bb231f9da14a80848262965255fb37726ab155fb9804c9a55b1",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:info" "permission": "demo:goods:info"
@@ -384,7 +362,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -495,13 +472,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -513,7 +483,7 @@
] ]
} }
}, },
"outputChecksum": "909ff0a80769ea17a9a6159097c45c5097286e53f5010a514491df4d1f177518", "outputChecksum": "2182ab26245d80a9a40216cce7cadbe8217ea12813f1cf536b7a59ba0a4ed4ae",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:list" "permission": "demo:goods:list"
@@ -522,7 +492,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -641,13 +610,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -689,7 +651,7 @@
"pagination" "pagination"
] ]
}, },
"outputChecksum": "476124b6a81b38277a80b1f773dffad01acee546c6140689abea6532d4216d2f", "outputChecksum": "15ee81575d900276aaa2437afa5ee6f41321e5b00ab58706322e5d5c0809bf8d",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:page" "permission": "demo:goods:page"
@@ -698,7 +660,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -881,13 +842,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -895,7 +849,7 @@
], ],
"minProperties": 2 "minProperties": 2
}, },
"inputChecksum": "103a179289c7ad50a87b38efeb58b281ee2cc03c3dabbe20814e26f2b804cee8", "inputChecksum": "ced49f501c83dd1dce9725c913a1761566828004c851779a5db85ddd2bb9123c",
"outputSchema": { "outputSchema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -918,7 +872,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",

View File

@@ -1,13 +1,13 @@
{ {
"formatVersion": 2, "formatVersion": 2,
"moduleCode": "demo", "moduleCode": "demo",
"revision": 10, "revision": 1,
"releaseVersion": null, "releaseVersion": null,
"schemaVersion": 10, "schemaVersion": 1,
"schemaInputChecksum": "5a47ef42d5af4736ae307150625beb1cea177c191c30864747f3a4a66f9aad23", "schemaInputChecksum": "8a55cecb643f802ef0f39ade2f31fe8313ad437d72d12b64525c0b1a6892813d",
"schemaChecksum": null, "schemaChecksum": null,
"actionManifestChecksum": null, "actionManifestChecksum": null,
"runtimeContractChecksum": "9ec90874dd2f0087423106ecffa0c17c9aa4362c597eca9eef9f25326b556c1e", "runtimeContractChecksum": "c1327810dd08dfd0cc1afeee99d7e9707fe14595272a3aa92ee13fc1d54aa0e5",
"implementationManifestChecksum": "d18964a3d40e3c9a65d83996ca3141a56043986c395f5d4cac57c60aa72f4236", "implementationManifestChecksum": "d18964a3d40e3c9a65d83996ca3141a56043986c395f5d4cac57c60aa72f4236",
"models": [ "models": [
{ {
@@ -65,16 +65,6 @@
"writable": true, "writable": true,
"filterable": true, "filterable": true,
"sortable": true "sortable": true
},
{
"code": "description",
"type": "varchar",
"nullable": true,
"primary": false,
"readable": true,
"writable": true,
"filterable": false,
"sortable": false
} }
] ]
} }
@@ -138,13 +128,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -152,7 +135,7 @@
"price" "price"
] ]
}, },
"inputChecksum": "1631b9abe9afce3e828787e355f4c380fc3d6210f58260797109014763bddab7", "inputChecksum": "c2984884419729416e6cd81e4cddc6d82486b6dde2d9de0ca17533f69f55816a",
"outputSchema": { "outputSchema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -174,13 +157,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -191,7 +167,7 @@
"status" "status"
] ]
}, },
"outputChecksum": "744f5d2ad90a1c4e21a1ff66eaae83a3f900cdd685d6ee409f03555336c9a067", "outputChecksum": "bb691ac7eaa19bb231f9da14a80848262965255fb37726ab155fb9804c9a55b1",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:add" "permission": "demo:goods:add"
@@ -200,7 +176,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -261,13 +236,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -275,7 +243,7 @@
"price" "price"
] ]
}, },
"inputChecksum": "1631b9abe9afce3e828787e355f4c380fc3d6210f58260797109014763bddab7", "inputChecksum": "c2984884419729416e6cd81e4cddc6d82486b6dde2d9de0ca17533f69f55816a",
"outputSchema": { "outputSchema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -297,13 +265,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -314,7 +275,7 @@
"status" "status"
] ]
}, },
"outputChecksum": "744f5d2ad90a1c4e21a1ff66eaae83a3f900cdd685d6ee409f03555336c9a067", "outputChecksum": "bb691ac7eaa19bb231f9da14a80848262965255fb37726ab155fb9804c9a55b1",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:add" "permission": "demo:goods:add"
@@ -323,7 +284,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -666,13 +626,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -683,7 +636,7 @@
"status" "status"
] ]
}, },
"outputChecksum": "744f5d2ad90a1c4e21a1ff66eaae83a3f900cdd685d6ee409f03555336c9a067", "outputChecksum": "bb691ac7eaa19bb231f9da14a80848262965255fb37726ab155fb9804c9a55b1",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:info" "permission": "demo:goods:info"
@@ -692,7 +645,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -744,13 +696,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -761,7 +706,7 @@
"status" "status"
] ]
}, },
"outputChecksum": "744f5d2ad90a1c4e21a1ff66eaae83a3f900cdd685d6ee409f03555336c9a067", "outputChecksum": "bb691ac7eaa19bb231f9da14a80848262965255fb37726ab155fb9804c9a55b1",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:info" "permission": "demo:goods:info"
@@ -770,7 +715,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -884,13 +828,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -902,7 +839,7 @@
] ]
} }
}, },
"outputChecksum": "909ff0a80769ea17a9a6159097c45c5097286e53f5010a514491df4d1f177518", "outputChecksum": "2182ab26245d80a9a40216cce7cadbe8217ea12813f1cf536b7a59ba0a4ed4ae",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:list" "permission": "demo:goods:list"
@@ -911,7 +848,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -1020,13 +956,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -1038,7 +967,7 @@
] ]
} }
}, },
"outputChecksum": "909ff0a80769ea17a9a6159097c45c5097286e53f5010a514491df4d1f177518", "outputChecksum": "2182ab26245d80a9a40216cce7cadbe8217ea12813f1cf536b7a59ba0a4ed4ae",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:list" "permission": "demo:goods:list"
@@ -1047,7 +976,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -1169,13 +1097,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -1217,7 +1138,7 @@
"pagination" "pagination"
] ]
}, },
"outputChecksum": "476124b6a81b38277a80b1f773dffad01acee546c6140689abea6532d4216d2f", "outputChecksum": "15ee81575d900276aaa2437afa5ee6f41321e5b00ab58706322e5d5c0809bf8d",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:page" "permission": "demo:goods:page"
@@ -1226,7 +1147,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -1343,13 +1263,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -1391,7 +1304,7 @@
"pagination" "pagination"
] ]
}, },
"outputChecksum": "476124b6a81b38277a80b1f773dffad01acee546c6140689abea6532d4216d2f", "outputChecksum": "15ee81575d900276aaa2437afa5ee6f41321e5b00ab58706322e5d5c0809bf8d",
"callerPolicy": { "callerPolicy": {
"kind": "permission", "kind": "permission",
"permission": "demo:goods:page" "permission": "demo:goods:page"
@@ -1400,7 +1313,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -1730,13 +1642,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -1744,7 +1649,7 @@
], ],
"minProperties": 2 "minProperties": 2
}, },
"inputChecksum": "103a179289c7ad50a87b38efeb58b281ee2cc03c3dabbe20814e26f2b804cee8", "inputChecksum": "ced49f501c83dd1dce9725c913a1761566828004c851779a5db85ddd2bb9123c",
"outputSchema": { "outputSchema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -1767,7 +1672,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -1832,13 +1736,6 @@
}, },
"status": { "status": {
"type": "integer" "type": "integer"
},
"description": {
"type": [
"string",
"null"
],
"maxLength": 500
} }
}, },
"required": [ "required": [
@@ -1846,7 +1743,7 @@
], ],
"minProperties": 2 "minProperties": 2
}, },
"inputChecksum": "103a179289c7ad50a87b38efeb58b281ee2cc03c3dabbe20814e26f2b804cee8", "inputChecksum": "ced49f501c83dd1dce9725c913a1761566828004c851779a5db85ddd2bb9123c",
"outputSchema": { "outputSchema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -1869,7 +1766,6 @@
{ {
"tableCode": "goods", "tableCode": "goods",
"fields": [ "fields": [
"description",
"id", "id",
"price", "price",
"status", "status",
@@ -1925,7 +1821,7 @@
"list", "list",
"page" "page"
], ],
"descriptorChecksum": "57f429f54ea377c92d648e4ffcc92611cbc288f227790cc1ff386f083bb7ce29", "descriptorChecksum": "22211efee07c5c1aada51d508d9c053e0d7388ad6fc0da12c95a4d1de2e3ca53",
"actions": { "actions": {
"add": "GoodsAdd", "add": "GoodsAdd",
"delete": "GoodsDelete", "delete": "GoodsDelete",

View File

@@ -44,21 +44,6 @@ export class GoodsService extends SaasCrudServiceBase<typeof Crud.Goods> {
throw new SaasBusinessReject('STOCK_COMMAND_REQUIRED','现有商品库存只能通过受管库存命令变更'); throw new SaasBusinessReject('STOCK_COMMAND_REQUIRED','现有商品库存只能通过受管库存命令变更');
} }
} }
if (change.operation === 'add' || change.operation === 'update') {
const input = change.input;
if (Object.prototype.hasOwnProperty.call(input, 'title') && !input.title.trim()) {
throw new SaasBusinessReject('GOODS_INPUT_INVALID', '商品名称不能为空');
}
if (Object.prototype.hasOwnProperty.call(input, 'price') && !/^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(input.price)) {
throw new SaasBusinessReject('GOODS_INPUT_INVALID', '商品价格必须是非负金额');
}
if (Object.prototype.hasOwnProperty.call(input, 'stock') && (!Number.isSafeInteger(input.stock) || input.stock < 0 || input.stock > 2147483647)) {
throw new SaasBusinessReject('GOODS_INPUT_INVALID', '商品库存必须是有效的非负整数');
}
if (Object.prototype.hasOwnProperty.call(input, 'status') && input.status !== 0 && input.status !== 1) {
throw new SaasBusinessReject('GOODS_INPUT_INVALID', '商品状态仅允许下架或上架');
}
}
} }
@SaasAction(Actions.GoodsBatchInfo) @SaasAction(Actions.GoodsBatchInfo)

View File

@@ -1,70 +0,0 @@
'use strict';
require('reflect-metadata');
const assert = require('node:assert/strict');
const test = require('node:test');
const fs = require('node:fs');
const path = require('node:path');
const ts = require('typescript');
const { SaasBusinessReject } = require('@cool-midway/module-runtime');
const sourceRoot = path.resolve(__dirname, '../src');
const previous = require.extensions['.ts'];
let GoodsService;
try {
require.extensions['.ts'] = (module, filename) => {
assert.ok(filename.startsWith(sourceRoot + path.sep));
const compiled = ts.transpileModule(fs.readFileSync(filename, 'utf8'), {
fileName: filename,
compilerOptions: { target: ts.ScriptTarget.ES2018, module: ts.ModuleKind.CommonJS,
experimentalDecorators: true, emitDecoratorMetadata: true, esModuleInterop: true },
});
module._compile(compiled.outputText, filename);
};
({ GoodsService } = require('../src/service/goods.ts'));
} finally {
if (previous) require.extensions['.ts'] = previous;
else delete require.extensions['.ts'];
}
function fixture(context = {}) {
const service = new GoodsService();
const calls = [];
service.saasActionContext = context;
service.inventory = { assertNoActiveReservations: async id => { calls.push(id); } };
return { service, calls };
}
const rejects = code => error => error instanceof SaasBusinessReject && error.code === code;
test('new catalog records accept an explicit initial stock and nonnegative decimal price', async () => {
const { service, calls } = fixture();
await service.modifyBefore({ operation: 'add', input: { title: '验收商品', price: '12.50', stock: 20, status: 1 } });
await service.modifyBefore({ operation: 'add', input: { title: '赠品', price: '0.00', stock: 0, status: 0 } });
assert.deepEqual(calls, []);
});
test('invalid catalog values are business rejections before persistence', async t => {
const inputs = [{ title: ' ' }, { price: '-0.01' }, { stock: -1 }, { stock: 1.5 }, { stock: 2147483648 }, { status: 2 }];
for (const input of inputs) await t.test(JSON.stringify(input), async () => {
const { service } = fixture();
await assert.rejects(service.modifyBefore({ operation: 'add', input: { title: '商品', price: '1.00', ...input } }), rejects('GOODS_INPUT_INVALID'));
});
});
test('catalog edits keep managed inventory protected even with valid stock values', async () => {
const { service } = fixture();
await service.modifyBefore({ operation: 'update', input: { id: '1', title: '更新名称', price: '0.00', status: 0 } });
await assert.rejects(service.modifyBefore({ operation: 'update', input: { id: '1', stock: 2 } }), rejects('STOCK_COMMAND_REQUIRED'));
await assert.rejects(service.modifyBefore({ operation: 'update', input: { id: '1', price: '-1.00' } }), rejects('GOODS_INPUT_INVALID'));
});
test('managed stock updates retain the same domain validation', async () => {
const { service } = fixture({ managedCrudCaller: { kind: 'business', actionName: 'goods.reserveStock' }, workflow: { coordinatorModule: 'order' } });
await service.modifyBefore({ operation: 'update', input: { id: '1', stock: 0 } });
await assert.rejects(service.modifyBefore({ operation: 'update', input: { id: '1', stock: -1 } }), rejects('GOODS_INPUT_INVALID'));
});
test('deletion still requires the inventory reservation lock and rejects an active reservation', async () => {
const { service, calls } = fixture();
await service.modifyBefore({ operation: 'delete', input: { id: '1' } });
assert.deepEqual(calls, ['1']);
service.inventory.assertNoActiveReservations = async () => { throw new SaasBusinessReject('STOCK_RESERVATION_CONFLICT', '仍有库存预留'); };
await assert.rejects(service.modifyBefore({ operation: 'delete', input: { id: '1' } }), rejects('STOCK_RESERVATION_CONFLICT'));
});