# MySDGS API 規格書 ## 共通錯誤處理(HTTPS) | HTTP 狀態碼 | 說明 | 回應格式 | |--------------|-------------------|---------------------------------------------| | 400 | 無效的請求參數 | `{ "error": "Invalid request" }` | | 401 | 未授權存取 | `{ "error": "Unauthorized" }` | | 403 | 沒有操作權限 | `{ "error": "Forbidden" }` | | 404 | 找不到資源 | `{ "error": "Not found" }` | | 500 | 伺服器內部錯誤 | `{ "error": "Internal server error" }` | ## Module list 1. User Service(使用者帳號管理) 2. Interaction Service(互動交流) 3. Activity Form Service(活動資料表單) 4. SROI Service(SROI 計算與分析) ## 使用者帳號管理模組 ### RESTful API (HTTPS) #### POST /auth/register **描述**:註冊帳號(一般使用者、企業後台管理者、企業使用者) 帳號角色說明: - **平台管理者(platform_admin)**:預設一組帳號,負責審核企業後台管理者。 - **一般使用者(general_user)**:可註冊,驗證 Email 後啟用。 - **企業後台管理者(organization_admin)**:註冊時需填寫企業名稱,需由平台管理者審核;每間企業僅可有一名後台管理者。 - **企業使用者(organization_user)**:註冊時需填寫企業名稱,需由該企業的後台管理者審核加入。 註冊流程邏輯: - 一般使用者:帳號建立後需進行 Email 驗證。 - 企業使用者:帳號建立後需企業後台管理者審核。 - 企業後台管理者:帳號建立後需平台管理者審核。 ```json Request Body: { "account": "使用者名稱", "email": "user@example.com", "password": "string", "account_type": "general | company_user | company_admin", "company_name": "(企業相關帳號需填)" } Response 200: { "uid": "uuid", "email": "user@example.com", "account_type": "company_user | company_admin | general", "status": "pending_approval | pending_email_verification | active" } ``` **描述**:註冊帳號(一般/企業後台管理者/企業使用者) 註冊流程邏輯: - 一般使用者:帳號建立後需進行 Email 驗證 - 企業使用者:需填企業名稱,提交後由企業後台管理者審核 - 企業後台管理者:填企業名稱,由平台管理者審核,每個企業僅允許一名後台管理者 ```json Request Body: { "account": "名稱 (僅一般使用者)", "email": "user@example.com", "password": "string", "account_type": "platform_admin | general_user | organization_user | organization_admin", "company_name": "(僅企業類型需填)" } Response 200: { "uid": "uuid", "email": "user@example.com", "account_type": "platform_admin | general_user | organization_user | organization_admin", "status": "pending_approval | pending_email_verification | active" } ``` **描述**:註冊帳號(一般/企業/管理者) ```json Request Body: { "email": "user@example.com", "password": "string", "account_type": "general | company_user | company_admin" } Response 200: { "uid": "uuid", "email": "user@example.com", "account_type": "company_user" } ``` #### POST /auth/login ```json Request Body: { "email": "user@example.com", "password": "string" } Response 200: { "access_token": "jwt_access_token", "refresh_token": "jwt_refresh_token", "expires_in": 3600 } ``` #### POST /auth/refresh-token ```json Request Body: { "refresh_token": "jwt_refresh_token" } Response 200: { "access_token": "new_jwt_access_token", "expires_in": 3600 } ``` #### GET /auth/verify-email ```json Query: ?token=xxxxxx Response 200: { "message": "Email verified" } ``` #### PUT /auth/profile ```json Request Body: { "origin_account": "origin_name", "new_account": "new_name", "new_email": "new_email@example.com", "new_company_name": "new_company_name" } Response 200: { "message": "Profile updated" } ``` ### gRPC(UserService) ```proto message SignUpRequest { string email = 1; string password = 2; string account_type = 3; } message MemberResponse { string uid = 1; string email = 2; string account_type = 3; string status = 4; } message LoginRequest { string email = 1; string password = 2; } message TokenPair { string access_token = 1; string refresh_token = 2; int32 expires_in = 3; } message RefreshTokenRequest { string refresh_token = 1; } service MemberService { rpc SignUp(SignUpRequest) returns (MemberResponse); rpc Login(LoginRequest) returns (TokenPair); rpc RefreshToken(RefreshTokenRequest) returns (TokenPair); rpc VerifyEmail(VerifyEmailRequest) returns (BoolResponse); rpc UpdateProfile(UpdateProfileRequest) returns (UserResponse); rpc ChangePassword(ChangePasswordRequest) returns (BoolResponse); rpc GetUser(UserIdRequest) returns (MemberResponse); rpc GetUserRole(UserIdRequest) returns (UserRoleResponse); } ``` ## 互動交流模組 ### 模組功能說明 - 首頁包含兩個跳轉按鈕: - 經驗分享文章區 - 發問區 - 經驗分享文章: - 文章為貼文形式,主要為文字,可附多張圖片與多個影片 - 僅限企業使用者可發布 - 發布前系統需驗證文章是否與 SDGs、ESG 活動相關 - 文章可附分類與 hashtag 標籤 - 所有使用者皆可瀏覽 - 所有使用者皆可留言(純文字,100 字內) - 發問區: - 問題與回覆皆為純文字(討論串形式) - 所有使用者皆可提問與回覆 - 提問需經過系統審核是否為永續相關問題 - 搜尋功能: - 可搜尋已發布的文章與討論串 - 可依 hashtag 搜尋對應的文章 ### RESTful API (HTTPS) #### POST /articles **描述**:建立經驗分享文章(僅限企業使用者,需經 ESG/SDGs 文字自動審核) ```json Request Body: { "title": "文章標題", "content": "文章內文", "tags": ["education", "environment"], "image_urls": ["https://..."], "video_urls": ["https://..."] } Response 201: { "article_id": "uuid", "status": "pending_review | approved | rejected" } ``` #### PUT /articles/{id} 描述:編輯文章(貼文者) ```json! Request Body: { "title":"更新後的title", "content": "更新後的留言內容", "tags": ["education", "environment"], "image_urls": ["https://..."], "video_urls": ["https://..."] } Response 200: { "article_id": "uuid", } ``` #### DELETE /articles/{id} 描述:刪除文章(發文者或平台後台管理員) ```json! Response 200: { "status": "success" } ``` #### POST /articles/{id}/comments **描述**:發表留言(所有使用者) ```json Request Body: { "parent_comment_id": null, "text": "留言內容 (<= 100 字)" } Response 200: { "comment_id": "uuid", "parent article": null, "parent_comment_id": null, "created_time": "2025-06-20T16:50:00Z" } ``` #### PUT /articles/{id}/comments/{comment_id} 描述:編輯留言(留言者) ```json! Request Body: { "content": "更新後的留言內容" } Response 200: { "comment_id": "uuid", "parent_comment_id": null, "created_time": "2025-06-20T16:50:00Z" } ``` #### DELETE /articles/{id}/comments/{comment_id} 描述:刪除留言(留言者或平台後台管理員) ```json! Response 200: { "comment_id": "uuid", "parent_comment_id": null, "created_time": "2025-06-20T16:50:00Z" } ``` #### POST /questions **描述**:提問(所有使用者,需經 ESG/SDGs 文字審核) ```json Request Body: { "title": "請問社區永續的做法有哪些?", "text": "問題問題問題" } Response 201: { "question_id": "uuid", "status": "pending_review | approved | rejected" } ``` #### POST /questions/{id}/answers **描述**:回覆問題(所有使用者) ```json Request Body: { "text": "可從環境教育、在地採購開始推動" } Response 200: { "question_id": "uuid", "answer_id": "uuid", "created_time": "2025-06-20T16:50:00Z" } ``` #### GET /list_articles **描述**:列出所有資料庫的文章 ```https GET /list_article ``` ```json Response 200: { "articles": [ { "article_id": "uuid", "title": "..." , "content": "...", "tags": ["education", "environment"], "image_urls": ["https://..."], "video_urls": ["https://..."], "created_time": "2025-06-20T16:50:00Z" } ], } ``` #### GET /search **描述**:搜尋貼文與問題 ```https GET /search?q=永續&tag=education ``` ```json Response 200: { "articles": [ { "article_id": "uuid", "title": "..." } ], "questions": [ { "question_id": "uuid", "text": "..." } ] } ``` #### POST /moderation/classify-text(暫時不做) **描述**:文章或問題內容自動審查是否與 ESG/SDGs 有關 ```json Request Body: { "text": "文章或問題全文" } Response 200: { "is_sustainable_related": true, "confidence": ["true | false"], "tags_detected": ["climate", "community"] } ``` ```json Request Body: { "title": "string", "content": "string", "tags": ["education", "environment"] } Response 201: { "article_id": "uuid", "question_id": "uuid", "status": "pending_review | approved | rejected" } ``` #### POST /articles/{id}/comments ```json Request Body: { "text": "留言內容 (<= 100 字)" } Response 200: { "comment_id": "uuid" } ``` ### gRPC(InteractionService) ```proto syntax = "proto3"; message CreateArticleRequest { string title = 1; string content = 2; repeated string tags = 3; repeated string image_urls = 4; repeated string video_urls = 5; } message Article { string article_id = 1; string title = 2; string status = 3; string author_id = 4; repeated string tags = 5; repeated string image_urls = 6; repeated string video_urls = 7; } message CommentRequest { string article_id = 1; string text = 2; string parent_comment_id = 3; } message EditCommentRequest { string comment_id = 1; string article_id = 2; string new_text = 3; } message DeleteCommentRequest{ string comment_id = 1; string article_id = 2; } message Comment { string comment_id = 1; string article_id = 2; string user_id = 3; string text = 4; string created_time = 5; } message Question { string question_id = 1; string user_id = 2; string title = 3; string text = 4; string status = 5; // pending_review, approved } message CreateQuestionRequest { string title = 1; string text = 2; string user_id = 3; } message AnswerRequest { string question_id = 1; string text = 2; } message SearchRequest { string query = 1; string tag = 2; } message SearchResults { repeated Article articles = 1; repeated Question questions = 2; } message BoolResponse { bool success = 1; } message Empty {} message ArticleList { repeated Article articles = 1; } message ArticleId { string article_id = 1; } message CommentList { repeated Comment comments = 1; } service InteractionService { rpc ListArticles(Empty) returns (ArticleList); rpc GetArticle(ArticleId) returns (Article); rpc CreateArticle(CreateArticleRequest) returns (Article); rpc CommentOnArticle(CommentRequest) returns (Comment); rpc EditCommentOnArticle(EditCommentRequest) returns (BoolResponse); rpc DeleteComment(DeleteCommentRequest) returns (BoolResponse); rpc AskQuestion(CreateQuestionRequest) returns (Question); rpc AnswerQuestion(AnswerRequest) returns (BoolResponse); rpc SearchContent(SearchRequest) returns (SearchResults); rpc EditArticle(EditArticleRequest) returns (Article); rpc DeleteArticle(ArticleId) returns (BoolResponse); rpc GetComment(ArticleId) returns (CommentList); } ``` ```proto message CreateArticleRequest { string title = 1; string content = 2; repeated string tags = 3; } message Article { string article_id = 1; string title = 2; string status = 3; } ``` ## 活動資料收集表單設計與資料處理模組 ### 模組功能說明 本模組負責活動資料收集表單的設計與填寫流程,支援後續 SROI 計算所需的結構化資料。表單內容包含: #### 一、定義範圍 - 活動名稱:文字輸入 - 情境(要解決的問題):文字輸入 - 投入:預計投入的資源、資金(數字) - 受益對象:文字輸入 - 利害關係人: - 類別(支持者、執行者、受益者) - 名稱、人數、是否接受訪談(勾選) - 納入/排除(勾選)、排除原因 - 地點:文字輸入 - 活動時間:起始與結束日期 - 預期產出:百分比輸入 #### 二、描繪成果 - 利害關係人類別 - 成果種類、項目、事件鏈 - 成果指標佐證:文字或照片 - 納入或排除(勾選) #### 三、衡量成果的貨幣價值 - 資金/資源投入: - 利害關係人類別、投入項目與其貨幣價值 - 人員投入時間: - 類別、總人數、人時數、時薪(系統換算金額) - 財務代理變數: - 成果名稱、變數、價值(單價×數量)、資料來源 #### 四、確定影響範圍 - 無謂因子/歸因因子/衰減因子/移轉因子: - 每筆包含:利害關係人類別、成果項目、因子數值、說明 #### 五、成果統計 - 針對每種利害關係人統計各類成果比例 #### 六、後續管理 - 發現的現象(例如未預期成果) - 利害關係人、說明、後續管理方案 ### RESTful API (HTTPS) #### PUT /activities/{id}/form **描述**:填寫或更新完整的活動表單內容 ```json Request Body: { "activity_name": "活動A", "context": "解決某問題", "location": "台中", "start_date": "2025-06-01", "end_date": "2025-06-30", "beneficiaries": "在地居民", "expected_output": 0.85, "inputs": 120000, "stakeholders": [ { "type": "受益者", "name": "青年", "count": 100, "interview": true, "included": true, "exclude_reason": "" } ], "outcomes": [ { "stakeholder_type": "受益者", "type": "教育", "item": "技能提升", "chain": "訓練 → 就業", "evidence": "證照取得", "included": true } ], "monetary_values": { "resource_inputs": [ { "stakeholder_type": "支持者", "item": "設備捐贈", "value": 30000 } ], "labor_inputs": [ { "stakeholder_type": "執行者", "count": 10, "hours": 200, "hourly_wage": 300 } ], "proxies": [ { "outcome_item": "就業", "proxy": "平均月薪", "unit_value": 35000, "units": 2, "source": "行政院統計處" } ] }, "impact_scope": { "deadweight": [...], "attribution": [...], "drop_off": [...], "displacement": [...] }, "follow_up": [ { "issue": "低出席率", "stakeholder_type": "受益者", "description": "部分地區參與意願低", "solution": "補助交通費" } ] } Response 200: { "message": "Form submitted" } ``` ### RESTful API (HTTPS) #### POST /activities ```json Request Body: { "name": "活動名稱", "start_date": "2025-06-01", "end_date": "2025-06-30" } Response 201: { "activity_id": "uuid" } ``` #### PUT /activities/{id}/form ```json Request Body: { "inputs": { "資金": 50000 }, "stakeholders": [ { "type": "受益者", "name": "在地居民", "count": 100, "interview": true } ] } Response 200: { "message": "Form updated" } ``` ### gRPC(ActivityFormService) ```proto message Stakeholder { string type = 1; string name = 2; int32 count = 3; bool interview = 4; bool included = 5; string exclude_reason = 6; } message Outcome { string stakeholder_type = 1; string type = 2; string item = 3; string chain = 4; string evidence = 5; bool included = 6; } message ResourceInput { string stakeholder_type = 1; string item = 2; double value = 3; } message LaborInput { string stakeholder_type = 1; int32 count = 2; int32 hours = 3; double hourly_wage = 4; } message FinancialProxy { string outcome_item = 1; string proxy = 2; double unit_value = 3; double units = 4; string source = 5; } message ImpactFactor { string stakeholder_type = 1; string outcome_item = 2; double value = 3; string description = 4; } message FollowUpAction { string issue = 1; string stakeholder_type = 2; string description = 3; string solution = 4; } message FormSection { string activity_id = 1; oneof section { Stakeholder stakeholder = 2; Outcome outcome = 3; ResourceInput resource_input = 4; LaborInput labor_input = 5; FinancialProxy proxy = 6; ImpactFactor factor = 7; FollowUpAction follow_up = 8; } } message GetFormSectionRequest { string activity_id = 1; string section_type = 2; // stakeholders, outcomes, inputs, impact, follow_up } message FormSectionList { repeated FormSection sections = 1; } message SubmitFormRequest { string activity_id = 1; repeated Stakeholder stakeholders = 2; repeated Outcome outcomes = 3; repeated ResourceInput resource_inputs = 4; repeated LaborInput labor_inputs = 5; repeated FinancialProxy proxies = 6; repeated ImpactFactor deadweight = 7; repeated ImpactFactor attribution = 8; repeated ImpactFactor drop_off = 9; repeated ImpactFactor displacement = 10; repeated FollowUpAction follow_ups = 11; } message ActivityId { string id = 1; } service ActivityFormService { rpc SubmitForm(SubmitFormRequest) returns (BoolResponse); rpc GetFormSection(GetFormSectionRequest) returns (FormSectionList); rpc CreateActivity(ActivityRequest) returns (ActivityId); rpc GetActivity(ActivityId) returns (ActivityDetails); rpc UpdateActivity(ActivityUpdateRequest) returns (BoolResponse); rpc GetForm(ActivityId) returns (ActivityForm); rpc UpdateForm(ActivityForm) returns (BoolResponse); } ``` ```proto message ActivityRequest { string name = 1; string start_date = 2; string end_date = 3; } message ActivityId { string id = 1; } ``` ## SROI模型建立與敏感度分析模組 ### 模組功能說明 本模組負責根據活動資料表單進行 SROI 計算與敏感度分析。 #### SROI 計算公式 ``` SROI = 總現值 / 總投入 總現值 = 每年影響力折現後加總 每年影響力值 = 總影響力 - (無謂因子 + 歸因因子 + 衰減因子 + 移轉因子) ``` #### 功能描述 - 使用者可從下拉選單中選取所管理的活動進行 SROI 計算 - 系統根據活動資料自動抓取所需欄位並套用公式進行 SROI 計算 - 計算結果會顯示所有參數與最終的 SROI 值 - 權限設計: - 企業使用者可檢視參數值(不可查看詳細數據) - 企業使用者可針對自身活動進行參數微調以進行敏感度模擬分析 ### RESTful API (HTTPS) #### GET /sroi/{activityId} **描述**:取得某活動的 SROI 計算結果與使用參數(資料庫資料) ```json Response 200: { "sroi_value": 2.73, "total_present_value": 273000, "total_investment": 100000, "parameters": { "deadweight": 0.1, "attribution": 0.15, "drop_off": 0.1, "displacement": 0.05 } } ``` #### POST /sroi/{activityId}/simulate **描述**:模擬調整參數後的 SROI 敏感度分析(不儲存) ```json Request Body: { "deadweight": 0.2, "attribution": 0.1, "drop_off": 0.05, "displacement": 0.0 } Response 200: { "sroi_value": 1.93, "total_present_value": 193000, "total_investment": 100000, "parameters": { "deadweight": 0.2, "attribution": 0.1, "drop_off": 0.05, "displacement": 0.0 } } ``` ### gRPC(SroiService) ```proto message SroiCalculationRequest { string activity_id = 1; } message SroiSimulationRequest { string activity_id = 1; double deadweight = 2; double attribution = 3; double drop_off = 4; double displacement = 5; } message SroiResult { double sroi_value = 1; double total_present_value = 2; double total_investment = 3; map<string, double> parameters = 4; } service SroiService { rpc CalculateSroi(SroiCalculationRequest) returns (SroiResult); rpc SimulateSroi(SroiSimulationRequest) returns (SroiResult); } ``` ### RESTful API (HTTPS) #### GET /sroi/{activityId} ```json Response 200: { "sroi_value": 2.73, "parameters": { "總投入": 100000, "總現值": 273000 } } ``` #### POST /sroi/{activityId}/simulate ```json Request Body: { "無謂因子": 0.1, "歸因因子": 0.15 } Response 200: { "sroi_value": 2.15 } ``` ### gRPC(SroiService) ```proto message ActivityId { string id = 1; } message SroiResult { double sroi_value = 1; map<string, double> parameters = 2; } ```