| @@ -0,0 +1,166 @@ | |||||
| ### frontend: | |||||
| - Routes.js (path: 'xxx**Win**' for Window component) | |||||
| - load js resources: | |||||
| ```javascript | |||||
| // option 1 | |||||
| items: [ | |||||
| Ext.create('App.test.TestPanel',{ | |||||
| title: 'Title 1' | |||||
| }), | |||||
| { | |||||
| xtype: 'panel', | |||||
| title: 'Title 2' | |||||
| } | |||||
| ] | |||||
| // option 2 | |||||
| requires: ['App.xxxx.XxxxPanel'], | |||||
| items: [{ | |||||
| xtype: 'test.testPanel', | |||||
| title: 'Title 1' | |||||
| }] | |||||
| ``` | |||||
| - get component e.g. | |||||
| ```javascript | |||||
| me.down('test\\.testPanel') //alias: 'widget.test.testPanel' | |||||
| - i18n: static/public/i18n/{locale}.json | |||||
| ```javascript | |||||
| // component level | |||||
| bind: { | |||||
| text: '{i18n.xx.xx}' | |||||
| } | |||||
| // program level | |||||
| App.getI18n('xx.xx') // or App.getI18n().xx.xx | |||||
| - security | |||||
| - App.security.getUser() | |||||
| - App.security.isGranted(string | string[]) | |||||
| - App.security.isGrantedAll(string[]) | |||||
| ### backend (draft): | |||||
| - vs code dev: example.launch.json | |||||
| - db migration: resources/db/changelog/changes/\*\* (support subfolder) | |||||
| - suggested that one changeset with one DDL | |||||
| - filename: {yyyyMMdd}\_{number}\_{name}/{number}_{name}.sql | |||||
| - filename 2(optional): {yyyyMMdd}\_{number}\_{name}/{number}\_{name}/{number}\_{name}.sql | |||||
| ```sql | |||||
| --liquibase formatted sql | |||||
| --changeset {name}:{id} | |||||
| --comment: remarks (optional) | |||||
| CREATE TABLE `tableName` ( | |||||
| `id` INT PRIMARY KEY AUTO_INCREMENT, | |||||
| `created` DATETIME NOT NULL DEFAULT NOW(), | |||||
| `createdBy` VARCHAR(30), | |||||
| `version` INT NOT NULL DEFAULT 0, | |||||
| `modified` DATETIME NOT NULL DEFAULT NOW(), | |||||
| `modifiedBy` VARCHAR(30), | |||||
| `deleted` BOOLEAN NOT NULL DEFAULT FALSE, | |||||
| `column1` INT NOT NULL, | |||||
| `column2` VARCHAR(255) | |||||
| ); | |||||
| ``` | |||||
| - controller | |||||
| - AbstractController | |||||
| - api: /public/\*\* /protected/\*\* | |||||
| - RequestBody Class (`@NotNull`, `@NotBlank`, `@Min`, `@Max`, `@Pattern`, etc...) | |||||
| ```java | |||||
| @NotNull | |||||
| @Pattern(regexp = "^Y$|^N$") | |||||
| private String YesNo; | |||||
| ``` | |||||
| - authentication check: `@PreAuthorize` | |||||
| - common response class `DataRes`, `IdRes`, `RecrodsRes`, `SuccessRes` | |||||
| - RESTfull api (http method, http status) | |||||
| - http status: | |||||
| - 404: record not found | |||||
| - 409: record_line not found or record_line not under the record (relation check) | |||||
| - 422: something wrong, (e.g. qty not enough) | |||||
| - suggest /save api (also allowed for POST: new, PUT: update) | |||||
| 1. POST /xxxx/save | |||||
| 2. body: | |||||
| ```json | |||||
| { | |||||
| "id": 1, //update | |||||
| "xx": "xx", | |||||
| "updateLines": [ | |||||
| { | |||||
| "id": null, // new | |||||
| "xx": "xx" | |||||
| }, | |||||
| { | |||||
| // need a relation check | |||||
| "id": 1, //update | |||||
| "xx": "xx" | |||||
| } | |||||
| ], | |||||
| // need a relation check | |||||
| "deleteLineIds": [1, 2] | |||||
| } | |||||
| ``` | |||||
| ```java | |||||
| //example | |||||
| @RestController | |||||
| @RequestMapping("/xxxx") | |||||
| public class ExampleController extends AbstractController { | |||||
| @PostMapping('/xxx') // @GetMapping @DeleteMapping @PutMapping @PatchMapping | |||||
| //@ResponseStatus(HttpStatus.CREATED) | |||||
| //@PreAuthorize("hasAuthority('XXX_MAINT')") | |||||
| public Xxxx api (@RequestBody @Valid ReqClass req) { | |||||
| } | |||||
| @GetMapping('/{id}') | |||||
| public Xxxx getXxx (@PathVariable int id) { | |||||
| } | |||||
| } | |||||
| ``` | |||||
| - service | |||||
| - AbstractService, AbstractIdEntityService, AbstractBaseEntityService | |||||
| - `@Transactional(rollbackFor = Exception.class)` for write, read only not need transaction | |||||
| - dao | |||||
| - interface | |||||
| - AbstractDao | |||||
| - [Query Creation](https://docs.spring.io/spring-data/jpa/docs/2.7.0/reference/html/#jpa.query-methods.query-creation) | |||||
| - `@Query` | |||||
| ```java | |||||
| public Optional<User> findByUsernameAndDeletedFalse(String username); | |||||
| // same | |||||
| @Query("FROM #{#entityName} u WHERE u.deleted = FALSE AND u.username = :username") | |||||
| public Optional<User> findByUsername(@Param("username") String username); | |||||
| ``` | |||||
| - entity | |||||
| - IdEntity, BaseEntity | |||||
| - validation: `@NotNull`, `@NotBlank`, `@Min`, `@Max`, `@Pattern`, etc... | |||||
| - jdbcDao | |||||
| - reduce Map: queryForEntity, queryForList("SELECT xxx" , params, XxxRecord.class) | |||||
| - [Class Object vs Hashmap](https://stackoverflow.com/questions/10258071/class-object-vs-hashmap) | |||||
| - reduce `@Autowired` | |||||
| - use constructor | |||||
| - try separate class (e.g. FileListController, FileDownloadController) | |||||
| - return Optional instead of null | |||||
| - return Optional\<T\> : may null | |||||
| - return T : must not null | |||||
| - i18n: i18n/messages_{locale(underscore)}.properties | |||||
| - `messageSource.getMessage("name", null, LocaleUtils.getLocale());` | |||||
| - reporting: | |||||
| - excel: | |||||
| - files: | |||||
| - excel/{filename}.xlsx | |||||
| - excel/{filename}_zh-TW.xlsx | |||||
| - excel/{filename}_zh-CN.xlsx | |||||
| - load Workbook: `ExcelUtils.loadTemplate("excel/{filename}");` | |||||
| - send out Workbook: `ExcelUtils.send(response, workbook, "filename"); // will output filename.xlsx` | |||||
| - jasper | |||||
| - files: | |||||
| - reports/{filename}.jrxml | |||||
| - reports/{filename}_zh-TW.jrxml | |||||
| - reports/{filename}_zh-CN.jrxml | |||||
| - compile: `JasperUtils.compile("reports/{filename}", params, new JRBeanCollectionDataSource(xxx), Map.of("subReport1","reports/{filename}"));` | |||||
| - send out: `JasperUtils.responsePdf(response, jRerpot, "filename"); // will output filename.pdf` | |||||
| - mobile: | |||||
| - login: POST /mobile/login | |||||
| - logout: POST /mobile/logout | |||||
| - call api: http headers with | |||||
| 1. `X-ATT-DeviceId` | |||||
| 2. `X-ATT-Access-Token` | |||||