|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- ### 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`
|