@ruiapp/rapid-core 0.1.37 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,6 +24,8 @@ declare class PluginManager {
24
24
  configureModels(applicationConfig: RpdApplicationConfig): Promise<void>;
25
25
  /** 配置模型属性 */
26
26
  configureModelProperties(applicationConfig: RpdApplicationConfig): Promise<void>;
27
+ /** 配置服务 */
28
+ configureServices(applicationConfig: RpdApplicationConfig): Promise<void>;
27
29
  /** 配置路由 */
28
30
  configureRoutes(applicationConfig: RpdApplicationConfig): Promise<void>;
29
31
  /** 在应用配置加载完成后调用。此时插件可以进行一些数据的初始化工作。 */
@@ -18,6 +18,8 @@ export interface IRpdServer {
18
18
  getActionHandlerByCode(code: string): ActionHandler | undefined;
19
19
  getDataAccessor<T = any>(options: GetDataAccessorOptions): IRpdDataAccessor<T>;
20
20
  getEntityManager<TEntity = any>(singularCode: string): EntityManager<TEntity>;
21
+ registerService(name: string, service: any): any;
22
+ getService<TService>(name: string): TService;
21
23
  getApplicationConfig(): RpdApplicationConfig;
22
24
  appendApplicationConfig(config: Partial<RpdApplicationConfig>): any;
23
25
  appendModelProperties(modelSingularCode: string, properties: RpdDataModelProperty[]): any;
@@ -86,6 +88,8 @@ export interface RapidPlugin {
86
88
  configureModels?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
87
89
  /** 配置模型属性 */
88
90
  configureModelProperties?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
91
+ /** 配置服务 */
92
+ configureServices?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
89
93
  /** 配置路由 */
90
94
  configureRoutes?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
91
95
  /** 在应用配置加载完成后调用。此时插件可以进行一些数据的初始化工作。 */
package/dist/index.js CHANGED
@@ -530,6 +530,14 @@ class PluginManager {
530
530
  }
531
531
  }
532
532
  }
533
+ /** 配置服务 */
534
+ async configureServices(applicationConfig) {
535
+ for (const plugin of this.#plugins) {
536
+ if (plugin.configureServices) {
537
+ await plugin.configureServices(this.#server, applicationConfig);
538
+ }
539
+ }
540
+ }
533
541
  /** 配置路由 */
534
542
  async configureRoutes(applicationConfig) {
535
543
  for (const plugin of this.#plugins) {
@@ -712,7 +720,10 @@ class RapidResponse {
712
720
  this.status = init?.status;
713
721
  }
714
722
  json(obj, status, headers) {
715
- const body = JSON.stringify(obj);
723
+ let body = null;
724
+ if (obj) {
725
+ body = JSON.stringify(obj);
726
+ }
716
727
  this.headers.set("Content-Type", "application/json");
717
728
  const responseHeaders = new Headers(this.headers);
718
729
  if (headers) {
@@ -2205,7 +2216,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
2205
2216
  before: entity,
2206
2217
  changes: options.entityToSave,
2207
2218
  }, plugin);
2208
- changes = options.entityToSave;
2219
+ changes = getEntityPartChanges(entity, options.entityToSave);
2209
2220
  const oneRelationPropertiesToUpdate = [];
2210
2221
  const manyRelationPropertiesToUpdate = [];
2211
2222
  lodash.keys(changes).forEach((propertyCode) => {
@@ -2452,6 +2463,7 @@ class RapidServer {
2452
2463
  #databaseAccessor;
2453
2464
  #cachedDataAccessors;
2454
2465
  #cachedEntityManager;
2466
+ #services;
2455
2467
  queryBuilder;
2456
2468
  config;
2457
2469
  databaseConfig;
@@ -2473,6 +2485,7 @@ class RapidServer {
2473
2485
  this.#databaseAccessor = options.databaseAccessor;
2474
2486
  this.#cachedDataAccessors = new Map();
2475
2487
  this.#cachedEntityManager = new Map();
2488
+ this.#services = new Map();
2476
2489
  this.queryBuilder = new QueryBuilder({
2477
2490
  dbDefaultSchema: options.databaseConfig.dbDefaultSchema,
2478
2491
  });
@@ -2598,6 +2611,12 @@ class RapidServer {
2598
2611
  // await this.configureApplication();
2599
2612
  // }
2600
2613
  }
2614
+ registerService(name, service) {
2615
+ this.#services.set(name, service);
2616
+ }
2617
+ getService(name) {
2618
+ return this.#services.get(name);
2619
+ }
2601
2620
  async start() {
2602
2621
  this.#logger.info("Starting rapid server...");
2603
2622
  const pluginManager = this.#pluginManager;
@@ -2618,6 +2637,7 @@ class RapidServer {
2618
2637
  await pluginManager.onLoadingApplication(this.#applicationConfig);
2619
2638
  await pluginManager.configureModels(this.#applicationConfig);
2620
2639
  await pluginManager.configureModelProperties(this.#applicationConfig);
2640
+ await pluginManager.configureServices(this.#applicationConfig);
2621
2641
  await pluginManager.configureRoutes(this.#applicationConfig);
2622
2642
  // TODO: check application configuration.
2623
2643
  await pluginManager.onApplicationLoaded(this.#applicationConfig);
@@ -3652,6 +3672,145 @@ class RouteManager {
3652
3672
  }
3653
3673
  }
3654
3674
 
3675
+ const code$b = "generateSn";
3676
+ async function handler$b(plugin, ctx, options) {
3677
+ const { server, routerContext } = ctx;
3678
+ const input = ctx.input;
3679
+ if (options?.ruleCode) {
3680
+ input.ruleCode = options.ruleCode;
3681
+ }
3682
+ if (!input.ruleCode) {
3683
+ throw new Error(`Rule code is required when generating sequence numbers.`);
3684
+ }
3685
+ const sequenceService = server.getService("sequenceService");
3686
+ const sequences = await sequenceService.generateSn(server, input);
3687
+ ctx.output = {
3688
+ sequences,
3689
+ };
3690
+ }
3691
+
3692
+ var generateSn$1 = /*#__PURE__*/Object.freeze({
3693
+ __proto__: null,
3694
+ code: code$b,
3695
+ handler: handler$b
3696
+ });
3697
+
3698
+ var pluginActionHandlers$4 = [
3699
+ generateSn$1,
3700
+ ];
3701
+
3702
+ var SequenceRule = {
3703
+ maintainedBy: "sequencePlugin",
3704
+ namespace: "svc",
3705
+ name: "sequence_rule",
3706
+ singularCode: "sequence_rule",
3707
+ pluralCode: "sequence_rules",
3708
+ schema: "public",
3709
+ tableName: "sequence_rules",
3710
+ properties: [
3711
+ {
3712
+ name: "id",
3713
+ code: "id",
3714
+ columnName: "id",
3715
+ type: "integer",
3716
+ required: true,
3717
+ autoIncrement: true,
3718
+ },
3719
+ {
3720
+ name: "code",
3721
+ code: "code",
3722
+ columnName: "code",
3723
+ type: "text",
3724
+ required: true,
3725
+ },
3726
+ {
3727
+ name: "description",
3728
+ code: "description",
3729
+ columnName: "description",
3730
+ type: "text",
3731
+ required: false,
3732
+ },
3733
+ {
3734
+ name: "config",
3735
+ code: "config",
3736
+ columnName: "config",
3737
+ type: "json",
3738
+ required: false,
3739
+ },
3740
+ ],
3741
+ };
3742
+
3743
+ var SequenceAutoIncrementRecord = {
3744
+ maintainedBy: "sequencePlugin",
3745
+ namespace: "svc",
3746
+ name: "sequence_auto_increment_record",
3747
+ singularCode: "sequence_auto_increment_record",
3748
+ pluralCode: "sequence_auto_increment_records",
3749
+ schema: "public",
3750
+ tableName: "sequence_auto_increment_records",
3751
+ properties: [
3752
+ {
3753
+ name: "id",
3754
+ code: "id",
3755
+ columnName: "id",
3756
+ type: "integer",
3757
+ required: true,
3758
+ autoIncrement: true,
3759
+ },
3760
+ {
3761
+ name: "ruleCode",
3762
+ code: "ruleCode",
3763
+ columnName: "rule_code",
3764
+ type: "text",
3765
+ required: true,
3766
+ },
3767
+ {
3768
+ name: "scope",
3769
+ code: "scope",
3770
+ columnName: "scope",
3771
+ type: "text",
3772
+ required: false,
3773
+ },
3774
+ {
3775
+ name: "currentValue",
3776
+ code: "currentValue",
3777
+ columnName: "current_value",
3778
+ type: "integer",
3779
+ required: true,
3780
+ },
3781
+ {
3782
+ name: "updatedAt",
3783
+ code: "updatedAt",
3784
+ columnName: "updated_at",
3785
+ type: "datetime",
3786
+ required: true,
3787
+ },
3788
+ ],
3789
+ };
3790
+
3791
+ var pluginModels$2 = [
3792
+ SequenceRule,
3793
+ SequenceAutoIncrementRecord,
3794
+ ];
3795
+
3796
+ var generateSn = {
3797
+ namespace: "svc",
3798
+ name: "svc.generateSn",
3799
+ code: "svc.generateSn",
3800
+ type: "RESTful",
3801
+ method: "POST",
3802
+ endpoint: "/svc/generateSn",
3803
+ actions: [
3804
+ {
3805
+ code: "generateSn",
3806
+ },
3807
+ ],
3808
+ };
3809
+
3810
+ var pluginRoutes$4 = [
3811
+ generateSn,
3812
+ ];
3813
+
3655
3814
  const segmentType$5 = "literal";
3656
3815
  async function resolveSegmentValue$5(server, ruleCode, config, input) {
3657
3816
  return config.content || "";
@@ -3800,190 +3959,62 @@ var segmentResolvers = [
3800
3959
  autoIncrement,
3801
3960
  ];
3802
3961
 
3803
- async function generateSn$2(server, input) {
3804
- const sequenceNumbers = [];
3805
- const { ruleCode, parameters } = input;
3806
- let { amount } = input;
3807
- if (!amount) {
3808
- amount = 1;
3962
+ class SequenceService {
3963
+ #server;
3964
+ constructor(server) {
3965
+ this.#server = server;
3809
3966
  }
3810
- const sequenceRuleDataAccessor = server.getDataAccessor({
3811
- singularCode: "sequence_rule",
3812
- });
3813
- const sequenceRule = await sequenceRuleDataAccessor.findOne({
3814
- filters: [
3815
- {
3816
- operator: "eq",
3817
- field: "code",
3818
- value: ruleCode,
3819
- }
3820
- ]
3821
- });
3822
- if (!sequenceRule) {
3823
- throw new Error(`Failed to generate sequence number. Sequence with code '${sequenceRule.code}' not found.`);
3824
- }
3825
- const sequenceConfig = sequenceRule.config;
3826
- if (!sequenceConfig || !sequenceConfig.segments) {
3827
- throw new Error("Failed to generate sequence number. Sequence not configured.");
3828
- }
3829
- for (let i = 0; i < amount; i++) {
3830
- let sequenceNumber = "";
3831
- for (const segmentConfig of sequenceConfig.segments) {
3832
- const segmentResolver = lodash.find(segmentResolvers, (item) => item.segmentType === segmentConfig.type);
3833
- if (!segmentResolver) {
3834
- // TODO: deal with unkown segment type
3835
- continue;
3967
+ async generateSn(server, input) {
3968
+ const sequenceNumbers = [];
3969
+ const { ruleCode, parameters } = input;
3970
+ let { amount } = input;
3971
+ if (!amount) {
3972
+ amount = 1;
3973
+ }
3974
+ const sequenceRuleDataAccessor = server.getDataAccessor({
3975
+ singularCode: "sequence_rule",
3976
+ });
3977
+ const sequenceRule = await sequenceRuleDataAccessor.findOne({
3978
+ filters: [
3979
+ {
3980
+ operator: "eq",
3981
+ field: "code",
3982
+ value: ruleCode,
3983
+ }
3984
+ ]
3985
+ });
3986
+ if (!sequenceRule) {
3987
+ throw new Error(`Failed to generate sequence number. Sequence with code '${sequenceRule.code}' not found.`);
3988
+ }
3989
+ const sequenceConfig = sequenceRule.config;
3990
+ if (!sequenceConfig || !sequenceConfig.segments) {
3991
+ throw new Error("Failed to generate sequence number. Sequence not configured.");
3992
+ }
3993
+ for (let i = 0; i < amount; i++) {
3994
+ let sequenceNumber = "";
3995
+ for (const segmentConfig of sequenceConfig.segments) {
3996
+ const segmentResolver = lodash.find(segmentResolvers, (item) => item.segmentType === segmentConfig.type);
3997
+ if (!segmentResolver) {
3998
+ // TODO: deal with unkown segment type
3999
+ continue;
4000
+ }
4001
+ const segment = await segmentResolver.resolveSegmentValue(server, ruleCode, segmentConfig, input);
4002
+ sequenceNumber += segment;
3836
4003
  }
3837
- const segment = await segmentResolver.resolveSegmentValue(server, ruleCode, segmentConfig, input);
3838
- sequenceNumber += segment;
4004
+ sequenceNumbers.push(sequenceNumber);
3839
4005
  }
3840
- sequenceNumbers.push(sequenceNumber);
4006
+ return sequenceNumbers;
3841
4007
  }
3842
- return sequenceNumbers;
3843
4008
  }
3844
4009
 
3845
- const code$b = "generateSn";
3846
- async function handler$b(plugin, ctx, options) {
3847
- const { server, routerContext } = ctx;
3848
- const input = ctx.input;
3849
- if (options?.ruleCode) {
3850
- input.ruleCode = options.ruleCode;
3851
- }
3852
- if (!input.ruleCode) {
3853
- throw new Error(`Rule code is required when generating sequence numbers.`);
3854
- }
3855
- const sequences = await generateSn$2(server, input);
3856
- ctx.output = {
3857
- sequences,
3858
- };
3859
- }
3860
-
3861
- var generateSn$1 = /*#__PURE__*/Object.freeze({
3862
- __proto__: null,
3863
- code: code$b,
3864
- handler: handler$b
3865
- });
3866
-
3867
- var pluginActionHandlers$4 = [
3868
- generateSn$1,
3869
- ];
3870
-
3871
- var SequenceRule = {
3872
- maintainedBy: "sequencePlugin",
3873
- namespace: "svc",
3874
- name: "sequence_rule",
3875
- singularCode: "sequence_rule",
3876
- pluralCode: "sequence_rules",
3877
- schema: "public",
3878
- tableName: "sequence_rules",
3879
- properties: [
3880
- {
3881
- name: "id",
3882
- code: "id",
3883
- columnName: "id",
3884
- type: "integer",
3885
- required: true,
3886
- autoIncrement: true,
3887
- },
3888
- {
3889
- name: "code",
3890
- code: "code",
3891
- columnName: "code",
3892
- type: "text",
3893
- required: true,
3894
- },
3895
- {
3896
- name: "description",
3897
- code: "description",
3898
- columnName: "description",
3899
- type: "text",
3900
- required: false,
3901
- },
3902
- {
3903
- name: "config",
3904
- code: "config",
3905
- columnName: "config",
3906
- type: "json",
3907
- required: false,
3908
- },
3909
- ],
3910
- };
3911
-
3912
- var SequenceAutoIncrementRecord = {
3913
- maintainedBy: "sequencePlugin",
3914
- namespace: "svc",
3915
- name: "sequence_auto_increment_record",
3916
- singularCode: "sequence_auto_increment_record",
3917
- pluralCode: "sequence_auto_increment_records",
3918
- schema: "public",
3919
- tableName: "sequence_auto_increment_records",
3920
- properties: [
3921
- {
3922
- name: "id",
3923
- code: "id",
3924
- columnName: "id",
3925
- type: "integer",
3926
- required: true,
3927
- autoIncrement: true,
3928
- },
3929
- {
3930
- name: "ruleCode",
3931
- code: "ruleCode",
3932
- columnName: "rule_code",
3933
- type: "text",
3934
- required: true,
3935
- },
3936
- {
3937
- name: "scope",
3938
- code: "scope",
3939
- columnName: "scope",
3940
- type: "text",
3941
- required: false,
3942
- },
3943
- {
3944
- name: "currentValue",
3945
- code: "currentValue",
3946
- columnName: "current_value",
3947
- type: "integer",
3948
- required: true,
3949
- },
3950
- {
3951
- name: "updatedAt",
3952
- code: "updatedAt",
3953
- columnName: "updated_at",
3954
- type: "datetime",
3955
- required: true,
3956
- },
3957
- ],
3958
- };
3959
-
3960
- var pluginModels$2 = [
3961
- SequenceRule,
3962
- SequenceAutoIncrementRecord,
3963
- ];
3964
-
3965
- var generateSn = {
3966
- namespace: "svc",
3967
- name: "svc.generateSn",
3968
- code: "svc.generateSn",
3969
- type: "RESTful",
3970
- method: "POST",
3971
- endpoint: "/svc/generateSn",
3972
- actions: [
3973
- {
3974
- code: "generateSn",
3975
- },
3976
- ],
3977
- };
3978
-
3979
- var pluginRoutes$4 = [
3980
- generateSn,
3981
- ];
3982
-
3983
4010
  /**
3984
4011
  * Sequence plugin
3985
4012
  */
3986
4013
  class SequencePlugin {
4014
+ #sequenceService;
4015
+ get sequenceService() {
4016
+ return this.#sequenceService;
4017
+ }
3987
4018
  get code() {
3988
4019
  return "sequencePlugin";
3989
4020
  }
@@ -4007,6 +4038,10 @@ class SequencePlugin {
4007
4038
  async configureModels(server, applicationConfig) {
4008
4039
  server.appendApplicationConfig({ models: pluginModels$2 });
4009
4040
  }
4041
+ async configureServices(server, applicationConfig) {
4042
+ this.#sequenceService = new SequenceService(server);
4043
+ server.registerService("sequenceService", this.#sequenceService);
4044
+ }
4010
4045
  async configureRoutes(server, applicationConfig) {
4011
4046
  server.appendApplicationConfig({ routes: pluginRoutes$4 });
4012
4047
  }
@@ -4056,7 +4091,7 @@ class SequencePlugin {
4056
4091
  sequenceConfig.enabled &&
4057
4092
  isNullOrUndefined(propertyValue)) {
4058
4093
  const ruleCode = getSequenceRuleCode(model, property);
4059
- const numbers = await generateSn$2(server, {
4094
+ const numbers = await this.#sequenceService.generateSn(server, {
4060
4095
  ruleCode,
4061
4096
  amount: 1,
4062
4097
  parameters: entity,
@@ -5039,7 +5074,7 @@ class CronJobPlugin {
5039
5074
  }
5040
5075
  }
5041
5076
 
5042
- async function getStateMachineNextSnapshot(server, options) {
5077
+ async function getStateMachineNextSnapshot(options) {
5043
5078
  const { machineConfig, currentState, event } = options;
5044
5079
  machineConfig.initial = currentState;
5045
5080
  const machine = xstate.createMachine(machineConfig);
@@ -5078,7 +5113,7 @@ async function handler(plugin, ctx, options) {
5078
5113
  throw new Error(`State machine with code '${input.code}' was not found.`);
5079
5114
  }
5080
5115
  stateMachine.config.id = input.code;
5081
- const snapshot = await getStateMachineNextSnapshot(server, {
5116
+ const snapshot = await getStateMachineNextSnapshot({
5082
5117
  machineConfig: stateMachine.config,
5083
5118
  context: input.context,
5084
5119
  currentState: input.currentState,
@@ -5290,7 +5325,7 @@ class StateMachinePlugin {
5290
5325
  throw new Error(`State machine of property '${statePropertyToUpdate.code}' not configured.`);
5291
5326
  }
5292
5327
  machineConfig.id = getStateMachineCode(model, statePropertyToUpdate);
5293
- const nextSnapshot = await getStateMachineNextSnapshot(server, {
5328
+ const nextSnapshot = await getStateMachineNextSnapshot({
5294
5329
  machineConfig,
5295
5330
  context: {},
5296
5331
  currentState: currentEntity[statePropertyToUpdate.code],
@@ -3,7 +3,10 @@
3
3
  */
4
4
  import { CreateEntityOptions, RpdApplicationConfig, RpdDataModel } from "../../types";
5
5
  import { IRpdServer, RapidPlugin, RpdConfigurationItemOptions, RpdServerPluginConfigurableTargetOptions, RpdServerPluginExtendingAbilities } from "../../core/server";
6
+ import SequenceService from "./SequenceService";
6
7
  declare class SequencePlugin implements RapidPlugin {
8
+ #private;
9
+ get sequenceService(): SequenceService;
7
10
  get code(): string;
8
11
  get description(): string;
9
12
  get extendingAbilities(): RpdServerPluginExtendingAbilities[];
@@ -11,6 +14,7 @@ declare class SequencePlugin implements RapidPlugin {
11
14
  get configurations(): RpdConfigurationItemOptions[];
12
15
  registerActionHandlers(server: IRpdServer): Promise<any>;
13
16
  configureModels(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any>;
17
+ configureServices(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any>;
14
18
  configureRoutes(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any>;
15
19
  onApplicationLoaded(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<void>;
16
20
  beforeCreateEntity(server: IRpdServer, model: RpdDataModel, options: CreateEntityOptions): Promise<void>;
@@ -12,4 +12,8 @@ export interface SegmentResolver {
12
12
  segmentType: string;
13
13
  resolveSegmentValue(server: IRpdServer, ruleCode: string, config: SequenceSegmentConfig, input: GenerateSequenceNumbersInput): Promise<string>;
14
14
  }
15
- export declare function generateSn(server: IRpdServer, input: GenerateSequenceNumbersInput): Promise<string[]>;
15
+ export default class SequenceService {
16
+ #private;
17
+ constructor(server: IRpdServer);
18
+ generateSn(server: IRpdServer, input: GenerateSequenceNumbersInput): Promise<string[]>;
19
+ }
@@ -0,0 +1,3 @@
1
+ import { GetStateMachineNextSnapshotOptions, TryGetStateMachineNextSnapshotResult } from "./StateMachinePluginTypes";
2
+ export declare function getStateMachineNextSnapshot(options: GetStateMachineNextSnapshotOptions): Promise<import("xstate").MachineSnapshot<any, import("xstate").AnyEventObject, Record<string, import("xstate").AnyActorRef>, import("xstate").StateValue, string, unknown, import("xstate").MetaObject, never>>;
3
+ export declare function tryGetStateMachineNextSnapshot(options: GetStateMachineNextSnapshotOptions): Promise<TryGetStateMachineNextSnapshotResult>;
package/dist/server.d.ts CHANGED
@@ -32,6 +32,8 @@ export declare class RapidServer implements IRpdServer {
32
32
  getEntityManager<TEntity = any>(singularCode: string): EntityManager<TEntity>;
33
33
  registerEventHandler<K extends keyof RpdServerEventTypes>(eventName: K, listener: (...args: RpdServerEventTypes[K]) => void): this;
34
34
  emitEvent<K extends keyof RpdServerEventTypes>(eventName: K, payload: RpdServerEventTypes[K][1], sender?: RapidPlugin): Promise<void>;
35
+ registerService(name: string, service: any): void;
36
+ getService<TService>(name: string): TService;
35
37
  start(): Promise<void>;
36
38
  configureApplication(): Promise<void>;
37
39
  registerFacilityFactory(factory: FacilityFactory): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -105,6 +105,18 @@ class PluginManager {
105
105
  }
106
106
  }
107
107
 
108
+
109
+ /** 配置服务 */
110
+ async configureServices(
111
+ applicationConfig: RpdApplicationConfig,
112
+ ) {
113
+ for (const plugin of this.#plugins) {
114
+ if (plugin.configureServices) {
115
+ await plugin.configureServices(this.#server, applicationConfig);
116
+ }
117
+ }
118
+ }
119
+
108
120
  /** 配置路由 */
109
121
  async configureRoutes(
110
122
  applicationConfig: RpdApplicationConfig,
@@ -49,7 +49,10 @@ export class RapidResponse {
49
49
  status?: HttpStatus,
50
50
  headers?: HeadersInit,
51
51
  ) {
52
- const body = JSON.stringify(obj);
52
+ let body: string | null = null;
53
+ if (obj) {
54
+ body = JSON.stringify(obj);
55
+ }
53
56
  this.headers.set("Content-Type", "application/json");
54
57
  const responseHeaders = new Headers(this.headers);
55
58
  if (headers) {
@@ -33,6 +33,8 @@ export interface IRpdServer {
33
33
  options: GetDataAccessorOptions,
34
34
  ): IRpdDataAccessor<T>;
35
35
  getEntityManager<TEntity = any>(singularCode: string): EntityManager<TEntity>;
36
+ registerService(name: string, service: any);
37
+ getService<TService>(name: string): TService;
36
38
  getApplicationConfig(): RpdApplicationConfig;
37
39
  appendApplicationConfig(config: Partial<RpdApplicationConfig>);
38
40
  appendModelProperties(modelSingularCode: string, properties: RpdDataModelProperty[]);
@@ -131,6 +133,8 @@ export interface RapidPlugin {
131
133
  configureModels?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
132
134
  /** 配置模型属性 */
133
135
  configureModelProperties?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
136
+ /** 配置服务 */
137
+ configureServices?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
134
138
  /** 配置路由 */
135
139
  configureRoutes?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
136
140
  /** 在应用配置加载完成后调用。此时插件可以进行一些数据的初始化工作。 */
@@ -615,7 +615,7 @@ async function updateEntityById(
615
615
  plugin,
616
616
  );
617
617
 
618
- changes = options.entityToSave;
618
+ changes = getEntityPartChanges(entity, options.entityToSave);
619
619
 
620
620
  const oneRelationPropertiesToUpdate: RpdDataModelProperty[] = [];
621
621
  const manyRelationPropertiesToUpdate: RpdDataModelProperty[] = [];
@@ -15,11 +15,17 @@ import pluginModels from "./models";
15
15
  import pluginRoutes from "./routes";
16
16
  import { PropertySequenceConfig } from "./SequencePluginTypes";
17
17
  import { isEqual } from "lodash";
18
- import { generateSn } from "./SequenceService";
18
+ import SequenceService from "./SequenceService";
19
19
  import { isNullOrUndefined } from "~/utilities/typeUtility";
20
20
 
21
21
 
22
22
  class SequencePlugin implements RapidPlugin {
23
+ #sequenceService!: SequenceService;
24
+
25
+ get sequenceService() {
26
+ return this.#sequenceService;
27
+ }
28
+
23
29
  get code(): string {
24
30
  return "sequencePlugin";
25
31
  }
@@ -49,6 +55,11 @@ class SequencePlugin implements RapidPlugin {
49
55
  async configureModels(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any> {
50
56
  server.appendApplicationConfig({ models: pluginModels });
51
57
  }
58
+
59
+ async configureServices(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any> {
60
+ this.#sequenceService = new SequenceService(server);
61
+ server.registerService("sequenceService", this.#sequenceService);
62
+ }
52
63
 
53
64
  async configureRoutes(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any> {
54
65
  server.appendApplicationConfig({ routes: pluginRoutes });
@@ -103,7 +114,7 @@ class SequencePlugin implements RapidPlugin {
103
114
  isNullOrUndefined(propertyValue)
104
115
  ) {
105
116
  const ruleCode = getSequenceRuleCode(model, property);
106
- const numbers = await generateSn(server, {
117
+ const numbers = await this.#sequenceService.generateSn(server, {
107
118
  ruleCode,
108
119
  amount: 1,
109
120
  parameters: entity,
@@ -18,54 +18,63 @@ export interface SegmentResolver {
18
18
  resolveSegmentValue(server: IRpdServer, ruleCode: string, config: SequenceSegmentConfig, input: GenerateSequenceNumbersInput): Promise<string>;
19
19
  }
20
20
 
21
- export async function generateSn(server: IRpdServer, input: GenerateSequenceNumbersInput): Promise<string[]> {
22
- const sequenceNumbers = [];
23
- const { ruleCode, parameters } = input;
24
- let { amount } = input;
21
+ export default class SequenceService {
22
+ #server: IRpdServer;
25
23
 
26
- if (!amount) {
27
- amount = 1;
24
+ constructor(server: IRpdServer) {
25
+ this.#server = server;
28
26
  }
29
27
 
30
- const sequenceRuleDataAccessor = server.getDataAccessor({
31
- singularCode: "sequence_rule",
32
- });
33
-
34
- const sequenceRule = await sequenceRuleDataAccessor.findOne({
35
- filters: [
36
- {
37
- operator: "eq",
38
- field: "code",
39
- value: ruleCode,
40
- }
41
- ]
42
- });
43
-
44
- if (!sequenceRule) {
45
- throw new Error(`Failed to generate sequence number. Sequence with code '${sequenceRule.code}' not found.`);
46
- }
47
-
48
- const sequenceConfig: SequenceRuleConfig = sequenceRule.config;
49
- if (!sequenceConfig || !sequenceConfig.segments) {
50
- throw new Error("Failed to generate sequence number. Sequence not configured.");
51
- }
52
-
53
- for (let i = 0; i < amount; i++) {
54
- let sequenceNumber: string = "";
55
-
56
- for (const segmentConfig of sequenceConfig.segments) {
57
- const segmentResolver: SegmentResolver = find(segmentResolvers, (item) => item.segmentType === segmentConfig.type);
58
- if (!segmentResolver) {
59
- // TODO: deal with unkown segment type
60
- continue;
28
+ async generateSn(server: IRpdServer, input: GenerateSequenceNumbersInput): Promise<string[]> {
29
+ const sequenceNumbers = [];
30
+ const { ruleCode, parameters } = input;
31
+ let { amount } = input;
32
+
33
+ if (!amount) {
34
+ amount = 1;
35
+ }
36
+
37
+ const sequenceRuleDataAccessor = server.getDataAccessor({
38
+ singularCode: "sequence_rule",
39
+ });
40
+
41
+ const sequenceRule = await sequenceRuleDataAccessor.findOne({
42
+ filters: [
43
+ {
44
+ operator: "eq",
45
+ field: "code",
46
+ value: ruleCode,
47
+ }
48
+ ]
49
+ });
50
+
51
+ if (!sequenceRule) {
52
+ throw new Error(`Failed to generate sequence number. Sequence with code '${sequenceRule.code}' not found.`);
53
+ }
54
+
55
+ const sequenceConfig: SequenceRuleConfig = sequenceRule.config;
56
+ if (!sequenceConfig || !sequenceConfig.segments) {
57
+ throw new Error("Failed to generate sequence number. Sequence not configured.");
58
+ }
59
+
60
+ for (let i = 0; i < amount; i++) {
61
+ let sequenceNumber: string = "";
62
+
63
+ for (const segmentConfig of sequenceConfig.segments) {
64
+ const segmentResolver: SegmentResolver = find(segmentResolvers, (item) => item.segmentType === segmentConfig.type);
65
+ if (!segmentResolver) {
66
+ // TODO: deal with unkown segment type
67
+ continue;
68
+ }
69
+
70
+ const segment = await segmentResolver.resolveSegmentValue(server, ruleCode, segmentConfig, input);
71
+ sequenceNumber += segment;
61
72
  }
62
-
63
- const segment = await segmentResolver.resolveSegmentValue(server, ruleCode, segmentConfig, input);
64
- sequenceNumber += segment;
73
+
74
+ sequenceNumbers.push(sequenceNumber);
65
75
  }
66
-
67
- sequenceNumbers.push(sequenceNumber);
76
+
77
+ return sequenceNumbers;
68
78
  }
69
-
70
- return sequenceNumbers;
71
- }
79
+
80
+ }
@@ -1,6 +1,6 @@
1
1
  import { ActionHandlerContext } from "~/core/actionHandler";
2
2
  import { RapidPlugin } from "~/core/server";
3
- import { GenerateSequenceNumbersInput, GenerateSequenceNumbersOutput, generateSn } from "../SequenceService";
3
+ import SequenceService, { GenerateSequenceNumbersInput, GenerateSequenceNumbersOutput } from "../SequenceService";
4
4
 
5
5
  export interface GenerateSequenceNumbersOptions {
6
6
  ruleCode: string;
@@ -27,7 +27,9 @@ export async function handler(
27
27
  throw new Error(`Rule code is required when generating sequence numbers.`);
28
28
  }
29
29
 
30
- const sequences = await generateSn(server, input);
30
+ const sequenceService = server.getService<SequenceService>("sequenceService");
31
+
32
+ const sequences = await sequenceService.generateSn(server, input);
31
33
 
32
34
  ctx.output = {
33
35
  sequences,
@@ -17,7 +17,7 @@ import pluginRoutes from "./routes";
17
17
  import { filter, find, first, get, isEqual } from "lodash";
18
18
  import { PropertyStateMachineConfig } from "./StateMachinePluginTypes";
19
19
  import { isNullOrUndefined } from "~/utilities/typeUtility";
20
- import { getStateMachineNextSnapshot } from "./StateMachineService";
20
+ import { getStateMachineNextSnapshot } from "./stateMachineHelper";
21
21
 
22
22
 
23
23
  class StateMachinePlugin implements RapidPlugin {
@@ -164,7 +164,7 @@ class StateMachinePlugin implements RapidPlugin {
164
164
  }
165
165
  machineConfig.id = getStateMachineCode(model, statePropertyToUpdate);
166
166
 
167
- const nextSnapshot = await getStateMachineNextSnapshot(server, {
167
+ const nextSnapshot = await getStateMachineNextSnapshot({
168
168
  machineConfig,
169
169
  context: {},
170
170
  currentState: currentEntity[statePropertyToUpdate.code],
@@ -1,7 +1,7 @@
1
1
  import { ActionHandlerContext } from "~/core/actionHandler";
2
2
  import { RapidPlugin } from "~/core/server";
3
3
  import { SendStateMachineEventInput, SendStateMachineEventOptions } from "../StateMachinePluginTypes";
4
- import { getStateMachineNextSnapshot } from "../StateMachineService";
4
+ import { getStateMachineNextSnapshot } from "../stateMachineHelper";
5
5
 
6
6
  export const code = "sendStateMachineEvent";
7
7
 
@@ -42,7 +42,7 @@ export async function handler(
42
42
 
43
43
  stateMachine.config.id = input.code;
44
44
 
45
- const snapshot = await getStateMachineNextSnapshot(server, {
45
+ const snapshot = await getStateMachineNextSnapshot({
46
46
  machineConfig: stateMachine.config,
47
47
  context: input.context,
48
48
  currentState: input.currentState,
@@ -1,8 +1,7 @@
1
- import { IRpdServer } from "~/core/server";
2
1
  import { DefaultStateMachineSnapshot, GetStateMachineNextSnapshotOptions, TryGetStateMachineNextSnapshotResult } from "./StateMachinePluginTypes";
3
2
  import { createMachine, getInitialSnapshot, getNextSnapshot } from "xstate";
4
3
 
5
- export async function getStateMachineNextSnapshot(server: IRpdServer, options: GetStateMachineNextSnapshotOptions) {
4
+ export async function getStateMachineNextSnapshot(options: GetStateMachineNextSnapshotOptions) {
6
5
  const { machineConfig, currentState, event } = options;
7
6
  machineConfig.initial = currentState;
8
7
 
@@ -17,7 +16,7 @@ export async function getStateMachineNextSnapshot(server: IRpdServer, options: G
17
16
  return nextSnapshot;
18
17
  }
19
18
 
20
- export async function tryGetStateMachineNextSnapshot(server: IRpdServer, options: GetStateMachineNextSnapshotOptions): Promise<TryGetStateMachineNextSnapshotResult> {
19
+ export async function tryGetStateMachineNextSnapshot(options: GetStateMachineNextSnapshotOptions): Promise<TryGetStateMachineNextSnapshotResult> {
21
20
  const { machineConfig, currentState, event } = options;
22
21
  machineConfig.initial = currentState;
23
22
 
package/src/server.ts CHANGED
@@ -52,6 +52,7 @@ export class RapidServer implements IRpdServer {
52
52
  #databaseAccessor: IDatabaseAccessor;
53
53
  #cachedDataAccessors: Map<string, DataAccessor>;
54
54
  #cachedEntityManager: Map<string, EntityManager>;
55
+ #services: Map<string, any>;
55
56
  queryBuilder: IQueryBuilder;
56
57
  config: RapidServerConfig;
57
58
  databaseConfig: IDatabaseConfig;
@@ -78,6 +79,8 @@ export class RapidServer implements IRpdServer {
78
79
  this.#cachedDataAccessors = new Map();
79
80
  this.#cachedEntityManager = new Map();
80
81
 
82
+ this.#services = new Map();
83
+
81
84
  this.queryBuilder = new QueryBuilder({
82
85
  dbDefaultSchema: options.databaseConfig.dbDefaultSchema,
83
86
  });
@@ -233,6 +236,14 @@ export class RapidServer implements IRpdServer {
233
236
  // }
234
237
  }
235
238
 
239
+ registerService(name: string, service: any) {
240
+ this.#services.set(name, service);
241
+ }
242
+
243
+ getService<TService>(name: string): TService {
244
+ return this.#services.get(name);
245
+ }
246
+
236
247
  async start() {
237
248
  this.#logger.info("Starting rapid server...");
238
249
  const pluginManager = this.#pluginManager;
@@ -261,6 +272,7 @@ export class RapidServer implements IRpdServer {
261
272
  await pluginManager.onLoadingApplication(this.#applicationConfig);
262
273
  await pluginManager.configureModels(this.#applicationConfig);
263
274
  await pluginManager.configureModelProperties(this.#applicationConfig);
275
+ await pluginManager.configureServices(this.#applicationConfig);
264
276
  await pluginManager.configureRoutes(this.#applicationConfig);
265
277
 
266
278
  // TODO: check application configuration.
@@ -1,4 +0,0 @@
1
- import { IRpdServer } from "../../core/server";
2
- import { GetStateMachineNextSnapshotOptions, TryGetStateMachineNextSnapshotResult } from "./StateMachinePluginTypes";
3
- export declare function getStateMachineNextSnapshot(server: IRpdServer, options: GetStateMachineNextSnapshotOptions): Promise<import("xstate").MachineSnapshot<any, import("xstate").AnyEventObject, Record<string, import("xstate").AnyActorRef>, import("xstate").StateValue, string, unknown, import("xstate").MetaObject, never>>;
4
- export declare function tryGetStateMachineNextSnapshot(server: IRpdServer, options: GetStateMachineNextSnapshotOptions): Promise<TryGetStateMachineNextSnapshotResult>;