## Note
* RoleOrgPair以及RoleFunctionPair的新增都設計為可多筆新增,因為考量使用者情境應該會希望能夠為單一角色一次性加入多個組織或功能。惟API設計並未檢查傳入的list當中各項物件是否為重複新增,目前須仰賴前端不重複地帶入
* 目前在更新相關功能中帶入null都是視作未要求更新,而非清空。
## API list
### LogIn
提供使用者登入服務。
|method|API Name|功能|描述|
|---|---|---|---|
|Post| LogIn|使用者登入|輸入帳號密碼,回傳token|
### PwdMaintenance
提供密碼更改相關服務。
|method|API Name|功能|描述|
|---|---|---|---|
|Post|ResetPWD|重置密碼|輸入帳號即信箱,重置密碼並將新密碼發送至使用者信箱|
|Post|ResetPWD|更換密碼|輸入帳號、密碼以及新密碼,重置密碼為新密碼|
### UserInfoMaintenance
設定使用者資料CRUD相關服務,對應表格為`s_user`。
|method|API Name|功能|描述|
|---|---|---|---|
|Get |AllUserDataQuery|查詢所有使用者資料|無須帶入帳號,回傳`s_user`中所有資料|
|Get|SingleUserDataQuery|查詢單一使用者資料|於route上帶入帳號id,回傳該使用者資料|
|Post|UserCreate|建立使用者|-|
|Put| SingleUserDataUpdate|更新單一使用者資料|-|
|Put| UserDataDelete|刪除使用者資料|帶入帳號id,更改該紀錄屬性`state`為0。|
|Get| AllOrganizationalUnitQuery|查詢所有組織單位|查詢`s_organizationalunit`所有紀錄|
* 使用者採取軟刪除(soft delete),以`state`表示是否刪除。
* 查詢所有組織單位因為僅有查詢功能,且與角色建立功能情境重疊度高,故放在同一個controller.
### RoleInfoMaintenance
角色資料設定CRUD相關服務,對應表格為`s_role`。
|method|API Name|功能|描述|
|---|---|---|---|
|Get|AllRoleDataQuery|查詢所有角色|無須帶入參數,回傳`s_role`所有資料|
|Post|RoleCreate|建立角色|-|
|Put|RoleUpdate|更新角色資料|-|
|Put|RoleDelete|刪除角色|帶入角色id,更改該紀錄屬性`state`為0。|
* 角色紀錄採取軟刪除(soft delete),以`state`表示是否刪除。
### RoleOrganizationRelation
設定紀錄各角色與各組織的權限關聯,包含新增、查詢、修改、刪除四項。對應表格為`s_roleorg`。
|method|API Name|功能|描述|
|---|---|---|---|
|Get|GetAllRoleOrganizationPair|查詢所有角色與組織單位配對|無須帶入參數,回傳`s_roleorg`所有資料|
|Post|CreateRoleOrganizationPair|新增角色與組織單位配對|-|
|Put|UpdateRoleOrganizationPair|修改角色與組織單位配對之權限|-|
|Delete|DeleteRoleOrganizationPair|刪除角色與組織單位配對|傳入`roleorgId`,直接刪除紀錄。|
### FunctionItemMaintenance
功能設定CRUD服務,對應表格為`s_functionitem`。
|method|API Name|功能|描述|
|---|---|---|---|
|Get|QueryAllFunctions|查詢所有功能|無須帶入參數,回傳`s_functionitem`中所有資料|
|Get|QueryRolesUsingSpecificFunction|查詢使用特定功能的角色|帶入功能`seq`,回傳所有與此功能關聯的角色。|
|Post|AddFunction|新增功能|-|
|Put|UpdateFunction|更新功能|-|
|Delete|DeleteFunction|刪除功能|傳入功能的`seq`,直接刪除紀錄。|
### RoleFunctionRelation
設定記錄各角色與各功能的使用關聯,對應表格為`s_rolefunction`。
|method|API Name|功能|描述|
|---|---|---|---|
|Get|GetAllRoleFunctionPair|查詢所有角色與功能配對|無須帶入參數,回傳`s_rolefunction`中所有資料|
|Post|CreateRoleFunctionPairs|建立角色與功能配對|-|
|Put|UpdateRoleFunctionPairs|更新角色與功能配對|-|
|Delete|DeleteRoleFunctionPairs|刪除角色與功能配對|傳入功能的`rolefunctionId`,直接刪除紀錄。|
## ER Diagram
```mermaid
---
title: User-related Function ER
---
erDiagram
s_user }o--|| s_role : ""
s_user }o--|| s_organizationalunit : ""
%%s_role ||--o{ s_roleorg : ""
%%s_organizationalunit ||--o{ s_roleorg : ""
s_role ||--o{ s_rolefunction : ""
s_rolefunction }o--|| s_functionitem :""
s_organizationalunit ||--o{ s_project : ""
s_role ||--o{ s_roleproject :""
s_project ||--o{ s_roleproject :""
s_user{
%% User Information
varchar(45) acctId PK "Guid"
varchar(255) acct UK "Not Null"
varchar(20) adacct
varchar(64) pw UK "Not Null"
varchar(100) name
varchar(100) email "Not Null"
%%string picURL "URL"
varchar(100) organizationalunit
varchar(100) countryregion
%% Authorization Infomation
varchar(45) roleId FK "Guid"
%% state Information
bool active "Default: true"
int state
%% system Information
Datetime loginon
}
s_role{
varchar(45) roleId PK
varchar(100) roleName "Admin/Dev/User"
%% state Information
bool active "Default: true"
int state
%% system Information
Datetime CreateOn
Datetime ModifiedOn
varchar(100) CreateBy
varchar(100) ModifiedBy
}
s_organizationalunit{
varchar(45) organizationalunitId PK
varchar(100) msm_name
varchar(250) msm_description
int msm_organizationalunittype
int msm_statecode
int msm_statuscode
}
s_functionitem{
varchar(45) seq PK
varchar(5) functionid
varchar(100) functionname
varchar(100) subfunction
varchar(255) description
varchar(255) url
bool active
}
s_rolefunction{
varchar(45) rolefunctionId PK
varchar(45) roleId FK
varchar(45) functionSeq FK
int active
}
s_roleproject{
varchar(45) roleprojectId PK
varchar(45) roleId FK
varchar(45) projectId FK
varchar(45) organizationalunitId FK
int isCreate
int isRead
int isUpdate
int isDelete
int statecode
int statusecode
}
```
* Authorization of CRUD(Create, Read, Update, Delete)operations relate to an organization. The user's permission to CRUD are determined by the combination of their role and organization.
* Each function can be futher divided into discreate components to granularly control CRUD permissions, which similarly defined by the inersection of user's role and organizational affliation.
**NOTE**
* Omit `CreatedOn`, `CreatedBy`, `ModifiedOn`, `ModifiedBy` fileds.
## 資料權限(Data Right)架構設計
### ER Diagram
```mermaid
---
title: User-related Function ER
---
erDiagram
s_user }o--|| s_role : ""
s_role }o--|| s_datarighttype : ""
s_datarighttype ||--o{ s_dataright : ""
s_dataright }o--|| s_hierarchy : ""
s_hierarchy }o--|| s_project : ""
s_hierarchy }o--|| s_organizationalunit : ""
s_user{
%% User Information
varchar(45) acctId PK "Guid"
varchar(45) roleId FK
}
s_role{
varchar(45) roleId PK
varchar(45) datarighttypeId FK
}
s_datarighttype{
varchar(45) datarighttypeId PK
}
s_dataright{
varchar(45) datarightId PK
varchar(45) datarighttypeId FK
varchar(45) itemId FK
}
s_hierarchy{
varchar(45) hierarchyId PK
varchar(45) itemId FK
varchar(45) parentId FK
varchar(45) parentTable FK
varchar(45) childTable FK
}
s_organizationalunit{
varchar(45) organizationalunitId PK
}
s_project{
varchar(45) projectId PK
varchar(45) organizationalunitId FK
}
```
### How to join out the data rights
1. Given specified value of `datarighttypeId` field, $datarighttypeId$, select all records in `s_hierarchy` entity where `datarighttypeId` = $datarighttypeId$. Let the collection of all values of the `itemId` field in the selected records be $itemIds$.
$itemIds$ :
```sql!
SELECT itemId FROM s_datarighttype WHERE datarighttypeId = @datarighttypeId
```
*string[]* **Recursion**(*string[]* $itemIds$)
> 2. For each item in $itemIds$, $itemId$, select the record from `s_hierarchy` where `itemId` = $itemId$. Let the selected record be $Record_0$.
>
> $Record_0$ :
> ```sql!
> SELECT * FROM s_hierarchy WHERE itemId = @itemId
> ```
> 3. Check if the value of `childTable` is null:
> if the value of `childTable` is not null :
>> i. Select all records from `s_hierarchy` where `parentId` = $itemId$. Let the selected records be $Record_1$.
>>
>> $Record_1$ :
>> ```sql!
>> SELECT itemId FROM s_hierarchy WHERE parentId = @itemId
>> ```
>>
>> ii. Return to step2, $Record_1$ is the new $itemIds$.
>
> else :
>> 4. Collect the $itemId$.
5. Filter out the replicated record of the collection of itemIds.
6. Return the result.
### Structure of `s_hierarchy`
Given a hierarchy of orgainzational unit and its prjects.
```bash!
OrgA
├── OrgB
│ ├── projB1
│ └── projB2
├── OrgC
│ └── projC1
└── projA1
```
The records in `s_hierarchy` are suppose to be like the below table:
|hierarchyId|itemId|parentId|parentTable|childTable|
|---|---|---|---|---|
|1|OrgA_Id|-|s_org|s_org|
|2|OrgB_Id|OrgA_Id|s_org|s_proj|
|3|projB1_Id|OrgB_Id|s_org|-|
|4|projB2_Id|OrgB_Id|s_org|-|
|5|OrgC_Id|OrgA_Id|s_org|s_proj|
|6|projC1_Id|OrgC_Id|s_org|-|
|7|projA1_Id|OrgA_Id|s_org|-|
## Entity and Related API
### 可能問題
* 目前大部分更改都是傳入空值等同不修改。然而,modifiedBy如果傳入空值,目前並不會以null蓋過,而是維持原先的modifiedBy,可能造成誤會。
### s_user (使用者管理)
### s_functionitem (功能清單)
此表格紀錄所有功能。
#### table
| 欄位 | 名稱 | 類型 | 長度 | 預設值 | 可為空 | 主鍵 | 必填 | 註解 |
|-----------|-------------|----------------|-----|-----|-----|----|----|-------------------------|
| seq | GUID編號 | uniqueidentifier | | | v | | | |
| functionid | 功能編號 | nvarchar | 5 | | v | v | v | f0001 |
| functionname | 主選單 | nvarchar | 100 | | | | v | 後台功能項目維護 |
| subfunction | 次選單 | nvarchar | 100 | | | | | |
| description | 功能項 | nvarchar | 255 | | v | | | |
| url | 功能項url | nvarchar | 255 | | | | | |
| active | 功能項目啟用狀態 | nvarchar | 1 | 1 | | | v | 0:停用、1:啟用 |
| createon | 建立時間 | datetime | | | | | | YYYY-MM-DD hh:mm:ss |
| createby | 建立人員 | nvarchar | 50 | | | | | AccountMapping |
| modifiedon | 修改時間 | datetime | | | | | | YYYY-MM-DD hh:mm:ss |
| modifiedby | 修改人員 | nvarchar | 50 | | | | | AccountMapping |
#### ddl
```sql
use carbon;
CREATE TABLE s_functionitem
(
seq varchar(45) NOT NULL,
functionid varchar(5) NOT NULL,
functionname varchar(100) NOT NULL,
subfunction varchar(100),
description varchar(255),
url varchar(255),
active tinyint(1) NOT NULL DEFAULT '1',
createon datetime,
createby varchar(50),
modifiedon datetime,
modifiedby varchar(50),
PRIMARY KEY (functionid)
);
ALTER TABLE carbon.s_functionitem CHANGE createon createdon datetime NULL;
ALTER TABLE carbon.s_functionitem CHANGE createby createdby varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL;
-- Change the data format of Attribute 'Seq' from char to varchar.
ALTER TABLE carbon.s_functionitem CHANGE seq seq varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL;
```
---
### s_rolefunction (各角色之功能清單)
此表格紀錄各角色具有那些功能。
#### table
| 欄位 | 資料格式 | Nullable | Default | Comment |
|------------------|--------------------|----------|---------|--------------------------------------|
| rolefunctionId | varchar(45) | NO | | 角色與功能對應編號 |
| roleId | varchar(45) | YES | NULL | s_role.roleId |
| functionSeq | varchar(45) | YES | NULL | s_functionitem.seq |
| active | tinyint(1) | | 1 | 0:停用、1:啟用 |
| CreatedOn | datetime | YES | NULL | |
| CreatedBy | varchar(100) | YES | NULL | |
| ModifiedOn | datetime | YES | NULL | |
| ModifiedBy | varchar(100) | YES | NULL | |
#### ddl
```sql
CREATE TABLE carbon.s_rolefunction (
rolefunctionId varchar(45),
roleId varchar(45),
functionSeq varchar(45) DEFAULT NULL COMMENT 's_functionitem.seq',
active tinyint(1) NOT NULL DEFAULT '1' COMMENT '0:停用、1:啟用',
CreatedOn datetime,
CreatedBy varchar(100) DEFAULT NULL,
ModifiedOn datetime ,
ModifiedBy varchar(100) DEFAULT NULL,
PRIMARY KEY (rolefunctionId)
)
```
### s_roleproject (各角色之標的產品清單)
#### Table
| 欄位 | 名稱 | 類型 | 長度 | 預設 | 可為空 | 主(外)鍵 | 註解 |
|-------------------|-------------------|----------------|-----|-----------|-------|---------|---------------------------------|
| roleprojectId | Role Project ID | VARCHAR | 45 | | 否 | PK | Using VARCHAR for GUID |
| roleId | 角色ID | VARCHAR | 45 | | 否 | FK | |
| projectId | 標的產品ID | VARCHAR | 45 | | 否 | FK | |
| organizationalunitId | 組織單位ID | VARCHAR | 45 | | 否 | FK | |
| active | 是否啟用 | BIT | | 1 | 否 | | Using BIT for boolean-like columns|
|isCreate|可以新增|INT||0|-||0為否;1為是|
|isRead|可以讀取|INT||0|-||0為否;1為是|
|isUpdate|可以修改|INT||0|-||0為否;1為是|
|isDelete|可以刪除|INT||0|-||0為否;1為是|
| statuscode | | INT | | | 是 | | 備用 |
| statecode | | INT | | | 是 | | 備用 |
| createdBy | 建立人員 | VARCHAR | 45 | | 是 | | |
| createdOn | 建立時間 | DATETIME | | | 是 | | |
| modifiedBy | 修改人員 | VARCHAR | 45 | | 是 | | |
| modifiedOn | 修改時間 | DATETIME | | | 是 | | |
* 主鍵使用varchar(45)而非uniqueidentifier出於一致性考量。
* CreatedBy, CreatedOn, ModifiedBy, and ModifiedOn are assigned by system.
---
#### DDL
```sql!
CREATE TABLE s_roleproject
(
roleprojectId VARCHAR(45) PRIMARY KEY, -- using varchar for GUID for consistency.
roleId VARCHAR(45) NOT NULL,
projectId VARCHAR(45) NOT NULL,
organizationalunitId VARCHAR(45) NOT NULL,
active BIT NOT NULL DEFAULT 1, -- using BIT for boolean-like columns
statuscode INT,
statecode INT,
createdBy VARCHAR(45),
createdOn DATETIME DEFAULT,
modifiedBy VARCHAR(45),
modifiedOn DATETIME
);
```
---
**查詢組織是否與角色關聯邏輯**
※OrganizationalunitId簡化為OrgId
```mermaid
flowchart TD
Org[/OrgId/] -->|查`s_roleorg`|D1{是否有紀錄存在\nWHERE`suborgId`\n=@OrgId} -->|有|A[不可刪除、修改]
D1-->|沒有|B[可刪除、修改]
```
**查詢標的產品是否與角色關聯邏輯**
```mermaid
flowchart TD
proj[/ProjectId/] -->|查‵s_project‵\n取得該標的產品所屬組織| org[/OrgId/] -->|查`s_roleorg`|D1
D1-->|有|D2
D1-->|沒有|OK1[可刪除、修改]
D2-->|true| Denied1[不可刪除、修改]
D2-->|false\n查`s_roleproject`|D3
D3-->|true|Denied2[不可刪除、修改]
D3-->|false|OK2[可刪除、修改]
D1{是否有紀錄\nWHERE\n `suborgId`=@OrgId}
D2{`allowAll` = true}
D3{‵IsSelected` = true}
```