跟上次分享的文章:
類似,
不過這次的需求比較簡單,是做出類似 URL Redirect 的規則設計,
一樣是有多個可能會日後動態增加的屬性,
只要符合一個條件即符合規則。
案例:
- 公司會收到客服詢問案件 (暫簡稱 Case),詢問案件會被標記上各種屬性,例如 : 語言 (lang)、是問哪個產品的問題 (productId) 等。
- 不同的屬性值可以設成一個 assign 規則,用來決定把客服詢問案件指派 (assign) 給哪個負責人員 (staffId)。
- 每個規則可以 assign 給不同的 staffId,但只會有一個規則被選用,規則可以按某個順序排,先符合的規則先被選用。
直接上實作部份範例,Dabase 採用 PostgreSql、程式部份採用 Java:
首先是 Database Table 設定的部份:
--設定 Case assign 規則及其規則要 assign 給哪個 staffId CREATE TABLE case_assign_rule ( id SERIAL PRIMARY KEY, assignee_staff_id INT ); --設定 Case 屬性,可用來判斷要選取哪個 Case assign rule CREATE TABLE case_assign_rule_type ( id SERIAL PRIMARY KEY, label VARCHAR NOT NULL ); --設定 Case assign rule,這裡沒有設定優先級,暫且先用 case_assign_rule_id 來當作優先級, --越小的優先。 --Note: --一個規則為把同一個 case_assign_rule_id 的多個規則用 AND 的方式結合在一起成為一個規則 --例如: case_assign_rule_id = 1 assign 給 staffId = 111 有兩條規則: --"productid 等於 100" 和 "langId 不等於 3" -- 等同於 case_assign_rule_id = 1: "productid 等於 100" 且 "langId 不等於 3" 時 assign 給 staffId = 111 CREATE TABLE case_assign_rule_assignment_condition ( case_assign_rule_id INT, case_assign_rule_type_id INT, is_equal_case_assign_rule_type_value BOOLEAN, case_assign_rule_type_value INT, PRIMARY KEY(case_assign_rule_id, case_assign_rule_type_id, is_equal_case_assign_rule_type_value, case_assign_rule_type_value) );
如果想要設定兩條規則 (以下用 "==" 代表 "等於"、用 "!=" 代表 "不等於"):
- productId = 100 且 langId != 3 時, assign 給 staffId = 111。
- productId = 100 時 assign 給 staffId = 123。
而如果 Case 的 product = 100 但 langId == 3 時就會選用第二個 rule 被 assign 給 staffId = 123。
我們可以這樣設定:
--設定 Case assign rule condition --設定 productId = 100 且 langId != 3 時, assign 給 staffId = 111。 INSERT INTO case_assign_rule_assignment_condition(case_assign_rule_id, case_assign_rule_type_id, is_equal_case_assign_rule_type_value, case_assign_rule_type_value) VALUES(1, 1, true, 100); INSERT INTO case_assign_rule_assignment_condition(case_assign_rule_id, case_assign_rule_type_id, is_equal_case_assign_rule_type_value, case_assign_rule_type_value) VALUES(1, 2, false, 3); --設定 productId = 100 時 assign 給 staffId = 123。 INSERT INTO case_assign_rule_assignment_condition(case_assign_rule_id, case_assign_rule_type_id, is_equal_case_assign_rule_type_value, case_assign_rule_type_value) VALUES(2, 1, true, 100);
再來是 Java 程式判斷的部份:
CASE_ASSIGN_RULE_TYPE.java:
package constant; public enum CASE_ASSIGN_RULE_TYPE { LANG_ID(1), PRODUCT_ID(2); private int id; private CASE_ASSIGN_RULE_TYPE(int id) { this.id = id; } public static CASE_ASSIGN_RULE_TYPE getCaseAssignRuleTypeById(int id, CASE_ASSIGN_RULE_TYPE defaultEnum) { for (CASE_ASSIGN_RULE_TYPE value : values()) { if (value.id == id) { return value; } } return defaultEnum; } }
CaseAssignRuleConditionBean.java:
package bean; import constant.CASE_ASSIGN_RULE_ASSIGNEE_TYPE; public class CaseAssignRuleConditionBean { private int caseAssignRuleId; private CASE_ASSIGN_RULE_ASSIGNEE_TYPE caseAssignRuleAssigneeType; private boolean isEqualCaseAssignRuleTypeValue; private int caseAssigneeTypeValue; public int getcaseAssignRuleId() { return caseAssignRuleId; } public void setcaseAssignRuleId(int caseAssignRuleId) { this.caseAssignRuleId = caseAssignRuleId; } public CASE_ASSIGN_RULE_ASSIGNEE_TYPE getcaseAssignRuleAssigneeType() { return caseAssignRuleAssigneeType; } public void setcaseAssignRuleAssigneeType(CASE_ASSIGN_RULE_ASSIGNEE_TYPE caseAssignRuleAssigneeType) { this.caseAssignRuleAssigneeType = caseAssignRuleAssigneeType; } public void setcaseAssignRuleAssigneeType(int caseAssignRuleAssigneeTypeId) { this.caseAssignRuleAssigneeType = CASE_ASSIGN_RULE_ASSIGNEE_TYPE.getcaseAssignRuleAssigneeTypeById(caseAssignRuleAssigneeTypeId, null); } public boolean getIsEqualCaseAssignRuleTypeValue() { return isEqualCaseAssignRuleTypeValue; } public void setIsEqualCaseAssignRuleTypeValue(boolean isEqualCaseAssignRuleTypeValue) { this.isEqualCaseAssignRuleTypeValue = isEqualCaseAssignRuleTypeValue; } public int getCaseAssigneeTypeValue() { return caseAssigneeTypeValue; } public void stCaseAssigneeTypeValue(int caseAssigneeTypeValue) { this.caseAssigneeTypeValue = caseAssigneeTypeValue; } }
CaseAssignRuleBean.java:
package bean; import java.util.ArrayList; import java.util.List; public class CaseAssignRuleBean { private int id; private int caseStaffAccountId; private List<CaseAssignRuleConditionBean> caseAssignRuleConditionList; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getCaseStaffAccountId() { return caseStaffAccountId; } public void setCaseStaffAccountId(int caseStaffAccountId) { this.caseStaffAccountId = caseStaffAccountId; } public List<CaseAssignRuleConditionBean> getCaseAssignRuleConditionList() { if (caseAssignRuleConditionList == null) { this.caseAssignRuleConditionList = new ArrayList<>(); } return caseAssignRuleConditionList; } public void setCaseAssignRuleConditionList(List<CaseAssignRuleConditionBean> caseAssignRuleConditionList) { this.caseAssignRuleConditionList = caseAssignRuleConditionList; } }
CaseStaffBean.java:
package bean; public class CaseStaffBean { int accountId; public int getAccountId() { return accountId; } public void setAccountId(int accountId) { this.accountId = accountId; } }
CaseAssignRuleDAO.java:
package dao; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import bean.CaseStaffBean; import bean.CaseAssignRuleBean; import bean.CaseAssignRuleConditionBean; import constant.CASE_ASSIGN_RULE_TYPE; import bean.CaseBean; @Repository public class CaseAssignRuleDAO { private static CaseAssignRuleDAO instance; private static DataSource postgreSQLDataSource; private static JdbcTemplate postgreSQLJdbcTemplate; private static userDAO userDAO; private CaseAssignRuleDAO() { } @Autowired private void setUp(CaseAssignRuleDAO instance, @Qualifier("postgreSQLDataSource") DataSource postgreSQLDataSource, @Qualifier("postgreSQLJdbcTemplate") JdbcTemplate postgreSQLJdbcTemplate, userDAO userDAO) { CaseAssignRuleDAO.instance = instance; CaseAssignRuleDAO.postgreSQLDataSource = postgreSQLDataSource; CaseAssignRuleDAO.postgreSQLJdbcTemplate = postgreSQLJdbcTemplate; CaseAssignRuleDAO.userDAO = userDAO; } public static synchronized CaseAssignRuleDAO getInstance() { if (instance == null) { throw new RuntimeException( CaseAssignRuleDAO.class.getName() + " instance is not initialized correctly."); } return instance; } //查詢一個 Case 應該要 assign 給哪一個 CaseStaff public CaseStaffBean queryCaseStaffByCaseAssignRule(CaseBean case) { List<CaseAssignRuleBean> caseAssignRuleList = querycaseAssignRuleList(); //決定 Case 的屬性應該要選擇哪一個 Case assign rule CaseAssignRuleBean chosenCaseAssignRule = caseAssignRuleList.stream() .filter(caseAssignRule -> caseAssignRule.getcaseAssignRuleConditionList() .stream() //Case assign rule 要被選擇的條件是它的 condition 都要被滿足(allMatch) .allMatch(caseAssignRuleCondition -> { //比較 langId if (CASE_ASSIGN_RULE_TYPE.LANG_ID == caseAssignRuleCondition.getcaseAssignRuleAssigneeType()) { //如果條件要求 typeValue 要 "等於" if (caseAssignRuleCondition.getIsEqualCaseAssignRuleTypeValue()) { return caseAssignRuleCondition.getAssigneeTypeValue() == case.getLangId(); } //如果條件要求 typeValue 要 "不等於" return caseAssignRuleCondition.getAssigneeTypeValue() != case.getLangId(); } //比較 productId if (CASE_ASSIGN_RULE_TYPE.PRODUCT_ID == caseAssignRuleCondition.getcaseAssignRuleAssigneeType()) { //如果條件要求 productId 要 "等於" if (caseAssignRuleCondition.getIsEqualCaseAssignRuleTypeValue()) { return caseAssignRuleCondition.getAssigneeTypeValue() == case.getProductId(); } //如果條件要求 productId 要 "不等於" return caseAssignRuleCondition.getAssigneeTypeValue() != case.getProductId(); } //如果執行到這裡代表規則設定了不應該存在的 case_type,在這裡我視為不符合條件。 return false; }) ) //以第一個找到的 Case assign rule 為主 .findFirst().orElse(null); if (chosenCaseAssignRule == null) { return null; } return userDAO.queryCaseStaffByAccountId(chosenCaseAssignRule.getAssigneeStaffAccountId()); } //查詢所有的 case_assign_rule public List<CaseAssignRuleBean> querycaseAssignRuleList() { String sql = "SELECT * FROM case_assign_rule ORDER BY id"; return postgreSQLJdbcTemplate.query(sql, new RowMapper<CaseAssignRuleBean>() { @Override public CaseAssignRuleBean mapRow(ResultSet rs, int rowNum) throws SQLException { CaseAssignRuleBean caseAssignRule = new CaseAssignRuleBean(); caseAssignRule.setId(rs.getInt("id")); caseAssignRule.setAssigneeStaffAccountId(rs.getInt("assignee_staff_id")); caseAssignRule.setcaseAssignRuleConditionList( querycaseAssignRuleConditionListByRuleId(caseAssignRule.getId())); return caseAssignRule; } }); } //把特定 case_assign_rule id 的 condition list 查出來。 public List<CaseAssignRuleConditionBean> querycaseAssignRuleConditionListByRuleId(int caseAssignRuleId) { String sql = "SELECT * " + "FROM case_assign_rule_assignment_condition " + "WHERE case_assign_rule_id = ?"; return postgreSQLJdbcTemplate.query(sql, new RowMapper<CaseAssignRuleConditionBean>() { @Override public CaseAssignRuleConditionBean mapRow(ResultSet rs, int rowNum) throws SQLException { CaseAssignRuleConditionBean caseAssignRuleCondition = new CaseAssignRuleConditionBean(); caseAssignRuleCondition.setcaseAssignRuleId(rs.getInt("case_assign_rule_id")); caseAssignRuleCondition .setcaseAssignRuleAssigneeType(rs.getInt("case_assign_rule_assignee_type_id")); caseAssignRuleCondition.setIsEqualAssigneeTypeValue(rs.getBoolean("is_equal_assignee_type_value")); caseAssignRuleCondition.setAssigneeTypeValue(rs.getInt("assignee_type_value")); return caseAssignRuleCondition; } }, caseAssignRuleId); } }
接下來如果日後有更多的屬性想要來供判斷、以及有更多的 rule 想要設定給不同的 Case Staff 時,就只要再加資料至 case_assign_rule_type, case_assign_rule, case_assign_rule_assignment_condition 這三個 Database Table 就可以了。