標籤: 網頁設計

  • angular 接入 IdentityServer4

    angular 接入 IdentityServer4

    angular 接入 IdentityServer4

    Intro

    最近把活動室預約的項目做了一個升級,預約活動室需要登錄才能預約,並用 IdentityServer4 做了一個統一的登錄註冊中心,這樣以後就可以把其他的需要用戶操作的應用統一到 IdentityServer 這裏,這樣就不需要在每個應用里都做一套用戶的機制,接入 IdentityServer 就可以了。

    目前活動室預約的服務器端和基於 angular 的客戶端已經完成了 IdentityServer 的接入,並增加了用戶的相關的一些功能,比如用戶可以查看自己的預約記錄並且可以取消自己未開始的預約,

    還有一個小程序版的客戶端暫時還未完成接入,所以小程序版目前暫時是不能夠預約的

    為什麼要寫這篇文章

    目前在網上看到很多都是基於 implicit 模式接入 IdentityServer,這樣實現起來很簡單,但是現在 OAuth 已經不推薦這樣做了,OAuth 推薦使用 code 模式來代替 implicit

    implicit 模式會有一些安全風險,implicit 模式會將 accessToken 直接返回到客戶端,而 code 模式只是會返回一個 code,accessToken 和 code 的分離的兩步,implicit 模式很有可能會將 token 泄露出去

    詳細可以參考 StackOverflow 上的這個問答

    https://stackoverflow.com/questions/13387698/why-is-there-an-authorization-code-flow-in-oauth2-when-implicit-flow-works

    除此之外,還有一個小原因,大多是直接基於 oidc-client 的 一個 npm 包來實現的,我是用了一個針對 angular 封裝的一個庫 angular-oauth2-oidc,如果你在用 angular ,建議你可以嘗試一下,針對 angular 做了一些封裝和優化,對 angular 更友好一些

    準備接入吧

    API 配置

    預約系統的 API 和網站管理系統是在一起的,針對需要登錄才能訪問的 API 單獨設置了的 policy 訪問

    services.AddAuthentication()
        .AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, options =>
        {
            options.Authority = Configuration["Authorization:Authority"];
            options.RequireHttpsMetadata = false;
    
            options.NameClaimType = "name";
            options.RoleClaimType = "role";
        })
        ;
    
    services.AddAuthorization(options =>
    {
        options.AddPolicy("ReservationApi", builder => builder
            .AddAuthenticationSchemes(IdentityServerAuthenticationDefaults.AuthenticationScheme)
            .RequireAuthenticatedUser()
            .RequireScope("ReservationApi")
        );
    });
    

    需要授權才能訪問的接口設置 Authorize 並指定 Policy 為 ReservationApi

    [Authorize(Policy = "ReservationApi")]
    [HttpPost]
    public async Task<IActionResult> MakeReservation([FromBody] ReservationViewModel model)
    

    IdentityServer Client 配置

    首先我們需要在 IdentityServer 這邊添加一個客戶端,因為我們要使用 code 模式,所以授權類型需要配置 authorization-code 模式,不使用 implicit 模式

    允許的作用域(scope) 是客戶端允許訪問的 api 資源和用戶的信息資源,openid 必選,profile 是默認的用戶基本信息的集合,根據自己客戶端的需要進行配置,ReservationApi 是訪問 API 需要的 scope,其他的 scope 根據客戶端需要進行配置

    angular 客戶端配置

    安裝 angular-oauth2-oidc npm 包,我現在使用的是 9.2.0 版本

    添加 oidc 配置:

    export const authCodeFlowConfig: AuthConfig = {
      issuer: 'https://id.weihanli.xyz',
    
      // URL of the SPA to redirect the user to after login
      redirectUri: window.location.origin + '/account/callback',
    
      clientId: 'reservation-angular-client',
    
      dummyClientSecret: 'f6f1f917-0899-ef36-63c8-84728f411e7c',
    
      responseType: 'code',
    
      scope: 'openid profile ReservationApi offline_access',
    
      useSilentRefresh: false,
    
      showDebugInformation: true,
    
      sessionChecksEnabled: true,
    
      timeoutFactor: 0.01,
    
      // disablePKCI: true,
    
      clearHashAfterLogin: false
    };
    

    在 app.module 引入 oauth 配置

      imports: [
        BrowserModule,
        AppRoutingModule,
        AppMaterialModule,
        HttpClientModule,
        FormsModule,
        ReactiveFormsModule,
        BrowserAnimationsModule,
        OAuthModule.forRoot({
          resourceServer: {
            allowedUrls: ['https://reservation.weihanli.xyz/api'],
            sendAccessToken: true
          }
        })
      ]
    

    OAuthModule 里 resourceServer 中的 allowedUrls 是配置的資源的地址,訪問的資源符合這個地址時就會自動發送 accessToken,這樣就不需要自己實現一個 interceptor 來實現自動在請求頭中設置 accessToken 了

    在 AppComponment 的構造器中初始化 oauth 配置,並加載 ids 的發現文檔

    export class AppComponent {
      constructor(
            private oauth: OAuthService
        ) {
        this.oauth.configure(authConfig.authCodeFlowConfig);
        this.oauth.loadDiscoveryDocument();
        }
        // ...
    }
    

    添加一個 AuthGuard,路由守衛,需要登錄才能訪問的頁面自動跳轉到 /account/login 自動登錄

    AuthGuard:

    import { Injectable } from '@angular/core';
    import { CanActivate, Router } from '@angular/router';
    import { OAuthService } from 'angular-oauth2-oidc';
    
    @Injectable({
      providedIn: 'root'
    })
    export class AuthGuard implements CanActivate {
      constructor(private router: Router, private oauthService: OAuthService) {}
    
      canActivate() {
        if (this.oauthService.hasValidAccessToken()) {
          return true;
        } else {
          this.router.navigate(['/account/login']);
          return false;
        }
      }
    }
    
    

    路由配置:

    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    import { ReservationListComponent } from './reservation/reservation-list/reservation-list.component';
    import { NoticeListComponent } from './notice/notice-list/notice-list.component';
    import { NoticeDetailComponent } from './notice/notice-detail/notice-detail.component';
    import { AboutComponent } from './about/about.component';
    import { NewReservationComponent } from './reservation/new-reservation/new-reservation.component';
    import { LoginComponent } from './account/login/login.component';
    import { AuthGuard } from './shared/auth.guard';
    import { AuthCallbackComponent } from './account/auth-callback/auth-callback.component';
    import { MyReservationComponent } from './account/my-reservation/my-reservation.component';
    
    const routes: Routes = [
      { path: '', component: ReservationListComponent },
      { path: 'reservations/new', component:NewReservationComponent, canActivate: [AuthGuard] },
      { path: 'reservations', component: ReservationListComponent },
      { path: 'notice', component: NoticeListComponent },
      { path: 'notice/:noticePath', component: NoticeDetailComponent },
      { path: 'about', component: AboutComponent },
      { path: 'account/login', component: LoginComponent },
      { path: 'account/callback', component: AuthCallbackComponent },
      { path: 'account/reservations', component: MyReservationComponent, canActivate: [AuthGuard] },
      { path: '**', redirectTo: '/'}
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
    

    AccountLogin 會將用戶引導到 ids 進行登錄,登錄之後會跳轉到配置的重定向 url,我配置的是 account/callback

    import { Component, OnInit } from '@angular/core';
    import { OAuthService } from 'angular-oauth2-oidc';
    
    @Component({
      selector: 'app-login',
      templateUrl: './login.component.html',
      styleUrls: ['./login.component.less']
    })
    export class LoginComponent implements OnInit {
    
      constructor(private oauthService: OAuthService) {
      }
    
      ngOnInit(): void {
        // 登錄
        this.oauthService.initLoginFlow();
      }
    
    }
    

    Auth-Callback

    import { Component, OnInit } from '@angular/core';
    import { OAuthService } from 'angular-oauth2-oidc';
    import { Router } from '@angular/router';
    
    @Component({
      selector: 'app-auth-callback',
      templateUrl: './auth-callback.component.html',
      styleUrls: ['./auth-callback.component.less']
    })
    export class AuthCallbackComponent implements OnInit {
    
      constructor(private oauthService: OAuthService, private router:Router) {
      }
    
      ngOnInit(): void {
        this.oauthService.loadDiscoveryDocumentAndTryLogin()
        .then(_=> {
          this.oauthService.loadUserProfile().then(x=>{
            this.router.navigate(['/reservations/new']);
          });
        });
      }
    
    }
    

    More

    當前實現還不太完善,重定向現在始終是跳轉到的新預約的頁面,應當在跳轉登錄之前記錄一下當前的地址保存在 storage 中,在 auth-callback 里登錄成功之後跳轉到 storage 中之前的地址

    Reference

    • https://sunnycoding.cn/2020/03/14/angular-spa-auth-with-ocelot-and-ids4-part3/#i-2
    • https://github.com/OpenReservation/angular-client
    • https://github.com/manfredsteyer/angular-oauth2-oidc/
    • https://github.com/OpenReservation/ReservationServer

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • 思考:如何保證服務穩定性?

    最近一直在忙618大促的全鏈路壓測&穩定性保障相關工作,結果618還未開始,生產環境就出了幾次生產故障,且大多都是和系統穩定性、性能相關的bad case。

    生產全鏈路壓測終於告一段落,抽出時間將個人收集的穩定性相關資料整理review了一遍,順帶從不同的維度,談談穩定性相關的“務虛”認知和思考。。。

     

    一、SLA!

    在開始談穩定性保障之前,我們先來聊聊業內經常提及的一個Topic:SLA!

    業內喜歡用SLA (服務等級協議,全稱:service level agreement)來衡量系統的穩定性,對互聯網公司來說就是網站服務可用性的一個保證。

    9越多代表全年服務可用時間越長服務越可靠,停機時間越短。就以一個標準99.99%為例,停機時間52.6分鐘,平均到每周也就是只能有差不多1分鐘的停機時間,

    也就是說網絡抖動這個時間可能就沒了。保證一個系統四個9或者更高的五個9,需要一套全體共識嚴格標準的規章制度,沒有規矩不成方圓。創建的規範有如下幾種:

    1、研發規範、自身穩定;

    2、事務中不能包含遠程調用;

    3、超時時間和重試次數要合理;

    4、表數據操作必須double check,合理利用索引,避免出現慢查詢、分庫分表不走分表鍵;

    5、沒有有效的資源隔離, 避免不同業務共用一個線程池或連接池;

    6、合理的系統拓撲,禁止不合理的服務依賴,能去依賴就去依賴,否則同步依賴盡量改成異步弱依賴;

    7、精簡的代碼邏輯;

    8、核心路徑流程必須進行資源隔離,確保任何突發情況主流程不能受影響。

     

    二、單服務穩定性

    關鍵字:開關可控、單一職責、服務隔離、異常兜底、監控發現!

    對於穩定性來說,拋開整體系統架構設計,單就每個業務域服務的穩定性也是非常的重要。

    只有每個業務環節都穩如泰山,才可以保障整個穩定性。單服務的穩定可以從以下幾個方面來進行:

    1、禁用設計:應該提供控制具體功能是否開啟可用的配置,在相應的功能服務出現故障時,快速下線局部功能,以保證整體服務的可用性;

    2、必要的緩存:緩存是解決併發的利器,可以有效的提高系統的吞吐量。按照業務以及技術的緯度必要時可以增加多級緩存來保證其命中率;

    3、接口無狀態性:服務接口應該是無狀態的,當前接口訪問不應該依賴上層接口的狀態邏輯;

    4、接口單一職責性:對於核心功能的接口,不應該過多的耦合不屬於它的功能。如果一個接口做的事情太多應做拆分,保證單接口的穩定性和快速響應;

    5、第三方服務隔離性:任何依賴於第三方的服務(不論接口還是中間件等),都應該做到熔斷和降級,不能有強耦合的依賴;

    6、業務場景兜底方案:核心業務場景需要做到完整的兜底方法,從前端到後端都應該有兜底措施;

    7、服務監控與及時響應:每個服務應該做好對應的監控工作,如有異常應及時響應,不應累積。

     

    三、集群穩定性

    關鍵字:系統架構、部署發布、限流熔斷、監控體系、壓測機制!

    對於集群維度的穩定性來說,穩定性保障會更加複雜。單服務是局部,集群是全局。一個見微知著,一個高瞻遠矚。

    1、合理的系統架構:合理的系統架構是穩定的基石;

    2、小心的代碼邏輯:代碼時刻都要小心,多擔心一點這裡會不會有性能問題,那裡會不會出現併發,代碼就不會有多少問題;

    3、優秀的集群部署:一台機器永遠會有性能瓶頸,優秀的集群部署,可以將一台機器的穩定放大無限倍,是高併發與大流量的保障;

    4、科學的限流熔斷:高併發來臨時,科學的限流和熔斷是系統穩定的必要條件;

    5、精細的監控體系:沒有監控體系,你永遠不會知道你的系統到底有多少隱藏的問題和坑,也很難知道瓶頸在哪裡;

    6、強悍的壓測機制:壓測是高併發穩定性的試金石,能提前預知高併發來臨時,系統應該出現的模樣;

    7、膽小的開發人員:永遠需要一群膽小的程序員,他們討厭bug,害怕error,不放過每一個波動,不信任所有的依賴。

     

    四、穩定性專項

    專項指的是針對某些特定場景下的特定問題而梳理出對應的方案。下面是針對一些常見的穩定性專項的概述:

    1、預案:分為定時預案和緊急預案,定時預案是大促常規操作對於一系列開關的編排,緊急預案是應對突發情況的特殊處理,都依賴於事前梳理;

    2、預熱:分為JIT代碼預熱和數據預熱,阿里內部有專門的一個產品負責這塊,通過存儲線上的常態化流量或者熱點流量進行回放來提前預熱,

      起源於某年雙十一零點的毛刺問題,原因是訪問了數據庫的冷數據rt增高導致的一系列上層限流,現在預熱已經成了大促之前的一個必要流程。

    3、強弱依賴:梳理強弱依賴是一個偏人肉的過程,但是非常重要,這是一個系統自查識別潛在風險點併為後續整理開關限流預案和根因分析的一個重要參考,

      阿里內部有一個強弱依賴檢測的平台,通過對測試用例注入RPC調用的延遲或異常來觀察鏈路的依賴變化,自動梳理出強弱依賴關係。

    4、限流降級熔斷:應對突發流量防止請求超出自身處理能力系統被擊垮的必要手段;

    5、監控告警&鏈路追蹤:監控分為業務監控、系統監控和中間件監控和基礎監控,作為線上問題發現和排查工具,重要性不言而喻。

     

    五、穩定性建設

    穩定性建設,就和基礎技術建設一樣,是一個長期迭代和不斷調整的過程,業內常見的穩定性建設類型,主要有如下幾種:

    1、容量規劃:個人感覺容量規劃在大廠里也並沒有做的很好,更多依賴的是業務方自己拍腦袋,然後全鏈路壓測期間驗證,不夠就再加機器。

    2、混沌工程:混沌工程是近幾年比較火的名詞,通過不斷給系統找麻煩來驗證並完善系統能力,阿里在這塊花了很大的精力建設紅藍軍對抗攻防,進行定期和不定期的演練,

      最後以打分的形式來給各個部門系統做排名,除了系統層面的故障演練外還有資金演練,篡改線上sql語句製造資損來測試業務監控糾錯的能力,通過製造小錯來避免大錯。

      跳轉門:混沌工程-初識

    3、流量調度:通過metric秒級監控和聚類算法實時找出異常單機來降低RPC流量權重,提升集群整體吞吐能力減少異常請求。

    4、容災&異地多活:起源於15年某施工隊將光纖挖斷帶來的支付寶故障,由此出來的三地五中心和單元化架構,異地多活本身的成本比較高,

      然後又存在數據同步的延時問題和切流帶來的臟數據問題,對於業務和技術都有比較高的要求。常見的容災有如下幾種:

      1)緩存掛掉,集群重啟緩存預熱如何處理?本地緩存,多級緩存是否可以替代?

      2)分佈式鎖,是否有開關一鍵切換?比如:ZK/ETCD編寫的分佈式鎖;

      3)大促峰值流量,如何防止外部ddos攻擊?如何識別流量類型?

      4)資源隔離:資源隔離,服務分組,流量隔離;

      5)高可用思想:避免單點設計!

      6)容錯:容錯上游,防禦下游。容錯主要需要注意如下幾點:

         6-1:外部依賴的地方都要做熔斷,避免雪崩;

         6-2:對於依賴我們的上游要限流,防止上游突發流量超過自己系統能夠扛住的最大QPS;

         6-3:對於下游既要評估好接口超時時間,防止下游接口超時異常導致自己系統被拖累;

         6-4:下游的接口要考慮各種異常情況,需要考慮中間狀態,通過引入柔性事務,確保數據最終一致。

    5、異地多活

    異地多活的本質,是數據中心架構的演進

    1)演進:單機房——雙機房——異地災備——異地多活;

    2)定義:分多個地域、多個數據中心運行線上的業務,並且每個IDC均提供在線服務;

    3)優點:彈性擴展能力、流量就近接入、靈活調度、提升可用性與用戶體驗、容災;

    4)步驟

      4-1:基礎設施:機房之間專線互聯,保證網絡質量穩定;

      4-2:持久存儲:一主三從,主IDC同步複製,異地IDC異步複製;

      4-3:中間件:DB、MQ、分佈式存儲;

      4-4:應用部署:根據應用域劃分,不同應用部署在不同地域,保持親緣性;

      4-5:流量接入與調度:網絡協議兼容,DNS,動態調度用戶就近訪問;

      4-6:監控與運維保障:專線實時監控,確保發生故障時可以觸發Failover(失效備援)和流量調度。

     

    六、穩定性思考

    關鍵字:階段工作、角色轉變!

    穩定性建設是一個演進的階段性過程,主要分為三個階段:

    1、發現問題解決問題:當問題較多時候就很被動,很多時候我們通過不斷完善監控來確保我們來快速定位問題,但仍處於被動的一方;

    2、主動尋找問題:混沌工程、破壞性測試、極限壓測、紅藍對抗等手段,一方作為創造問題方不斷挑戰系統極限,另一方見招拆招快速修復。

    3、角色轉變:這個過程中會積累很多處理問題的經驗,不斷完善系統健壯性,爭取在用戶發現問題前消滅於萌芽中。角色轉變,變被動為主動。

     

    七、推薦閱讀

    聊聊服務災備

    大促穩定性建設

    運維監控體系建設

    這樣的高可用,我不要

    高併發限流,到底限的什麼鬼

    新浪微博平台穩定性體系介紹

    StabilityGuide—穩定大於一切

    沒有預熱,不叫高併發,叫併發高

    信號量限流,高併發限流不得不說的秘密

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計最專業,超強功能平台可客製化

  • 中國電動車購買補助高於預期 特斯拉股價攀新高

    據新浪財經消息,受中國財政部宣布的電動汽車補貼縮減幅度低於市場預期,可望為特斯拉下月起在中國銷售Model S創造良好環境的影響,特斯拉汽車(TSLA)昨(10)日股價漲5.38%至196.56美元的新高。特斯拉方面表示,雖然Model S不在直接補貼範圍內,但希望中國也能考慮給予直接補貼待遇。

    據《新華社》報導,中國去(2013)年向電動車消費者每台車補貼約 3.5 至 6 萬元人民幣,而今年與明 (2015) 年補貼金額將分別減少至 5% 與 10%,僅為原先公布補貼縮減幅度的一半。

    特斯拉上月宣布的報告指出,去年第 4 季一共售出 6900 輛 Model S 車款,銷售輛季增 25%,也比先前預期高出 20%,也帶動公司股價穩定回升。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 萬向李澤楷競買菲斯科 今日將出結果

    據《華爾街日報》報導,美國電動汽車品牌菲斯科(Fisker)最終將於美國當地時間2月12日在紐約進行拍賣,美國法院表示會在一個工作日後宣判結果,李澤楷控股的混合動力技術控股有限公司與萬向集團仍是最有可能的競買成功者。

    據路透社的消息,混合動力為加強此次競買工作以及公司的管理,特別聘請了曾主要負責福特歐洲業務的前高管Martin Leach。Leach表示,混合動力目前最大的困境是,萬向集團在一年前收購了A123系統公司,而A123是Fisker的主要電池供應商,不過混合動力可以得到另一家電池公司波士頓動力的支持。

    美國汽車經銷協會首席經濟學家史蒂文表示,競買的最終贏家將獲得的不僅是Fisker的汽車產品或設計,還包括其知識產權,其中涉及的36項專利(大約一半為待定),包括電氣傳動系統、太陽能等專利技術。

    據悉,萬向錢潮作為萬向集團控股的汽車零部件製造和銷售公司,其股價近日來也因受各種消息刺激連續上漲。

    萬向錢潮並於昨(12)日發佈公告表示,從2000年開展電動汽車研發以來,控股股東萬向集團一直致力於發展清潔能源產業,但與國際先進技術相比尚有一定差距。為此,萬向集團希望通過併購聯合方式提升技術能力等。此前,萬向集團掌門人魯冠球曾表示,對同特斯拉電動車結盟合作抱開放態度。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • 特斯拉Model S在港正式銷售 入門級57.9萬港幣

    昨(13)日有消息稱,特斯拉Model S車型已正式在香港銷售,其中入門級配置僅售57.9萬港幣。此外有報導稱,比亞迪將與ABB集團及德國戴姆勒共建全球最大電動汽車快速充電網,ABB將在未來6年內為兩家公司提供直流快速充電樁。

    上述消息為A股正紅火的新能源汽車相關概念再添一把柴。截至昨天收盤,充電樁概念逆市上漲0.67%,漲幅居前,奧特迅領漲,漲幅為7.60%。特斯拉概念表現較為抗跌僅下挫0.51%,金瑞科技漲停,比亞迪也大漲8.26%。鋰電池概念跌0.93%,其中南洋科技漲9.48%。

    證券方面人士認為,在政府大力建設新型城鎮化、發展城市公共交通的背景下,對新能源汽車的政策扶持將會給電池業、電池電機上游的鋰和稀土資源行業、充電站行業、乘用車企業帶來機遇。

    但需要注意的是,未來的上漲空間將取決於政策的落實情況和上市公司的基本面,並且投資者不可對其期望過高,尤其是目前市場處於震盪的情形下,一旦后續資金追高乏力,相關個股很難擺脫大勢的影響。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計最專業,超強功能平台可客製化

  • 萬向集團出價1.49億美元 最終競得菲斯科

    萬向集團出價1.49億美元 最終競得菲斯科

    據外電報道,中國汽車零件製造商萬向集團擊敗李澤楷旗下的Hybrid Tech,在美國豪華混合動力汽車製造商Fisker的破產資產拍賣中勝出。萬向出價1.492億美元,大約是Fisker最初尋求的收購價的6倍。

    這次拍賣持續了三天,經歷了19輪投標。美國破產法官Kevin Gross按計劃將於2月18日批准此次出售。

    菲斯科在2009年獲得了美國能源部的5.29億美元綠色貸款。但能源部在2011年中期凍結支付,稱菲斯科在開發新車型上狀況頻出,一再拖延。

    2013年11月,菲斯科申請破產,並要求破產法官準許Hybrid Tech以2500萬美元的低價,向美國能源部購入Fisker原本總值1.6億多美元的貸款。Hybrid Tech從而成為Fisker的高級擔保貸款人,更表明有意進一步收購Fisker。

    但無擔保債權人反對這一報價,從而幫助中國最大的汽車零部件供應商於12月進入到交易環節中。

    在此筆交易達成后,萬向集團將努力重振菲斯科在中國汽車市場的發展,該集團也獲得了一個打進美國市場的入口點。據資料顯示,萬向為中國投資美國製造業、新能源和房地產的先行者,在過去20年,萬向於美國的投資遍布美國14個州,涉及汽車零件製造、不動產、新能源和私募基金等。

    2012年底時,萬向曾以近2.6億美元擊敗江森自控,成功拍得美國破產電池生產商A123鋰電池公司資產,這家公司正是Fisker的電池供應商。經營汽車零部件業務的萬向集團一直期望進軍整車制造領域。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※教你寫出一流的銷售文案?

  • 英最大單電動車合約易主 比亞迪遺憾出局

    據《英國每日電訊》昨(17)日報導,比亞迪推出倫敦首支全電動出租車隊的計劃受挫,倫敦第二大出租車服務商綠番茄出租車公司(Greentomatocars)表示,決定不再和比亞迪進行合作,而改為準備測試現代汽車的一款燃料電池汽車。

    這是英國史上最大一筆電動車交易。去年曾有傳言稱,倫敦充電樁設施不全,導致這批出租車無法按時上路。

    比亞迪已向綠番茄公司交付了20輛E6汽車,這批車輛隨后將由另一家運營商Thriev接盤。Thriev已在倫敦建造兩個快速充電站,可為E6汽車在兩個小時內完全完電。據比亞迪介紹,E6在充電完畢之后能續航186英里,比倫敦市場上日產聆風的124英里要高出不少。

    Thriev公司發言人還表示,公司將在18-24個月內打造一支由1000輛電動汽車所組成的電動車隊。Thriev還與英國天然氣集團進行了接洽,商談如何在倫敦建立多個電動汽車充電站事宜。

    倫敦市長辦公室曾表示,出租車貢獻了倫敦所有尾氣排放的逾三分之一,推廣零排放出租車是政府將英國打造成重要電動汽車市場舉措的一部分。倫敦市長鮑里斯•約翰遜也設定了全市出租車必須在2018年前實現零排放的目標。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※別再煩惱如何寫文案,掌握八大原則!

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 第二屆新能源車與充電技術中國國際論壇

    地點:上海綠地萬豪酒店 /中國上海黃浦區江濱路99號(靠近打浦路)
    會議背景:

        新能源汽車是國務院確定的重要戰略性新興產業,是我國最具發展潛力的重要領域之一。近日來中國大面積飽受霧霾天氣困擾的嚴峻形勢,更使得加快新能源汽車的發展迫在眉睫。隨著新一輪新能源車補貼政策的出臺以及首批28個示範運行城市的公佈,新能源汽車產業展現出良好的發展態勢。

        中國政府和車企致力於電動車在全國範圍內的大規模推廣。工信部發佈,我國2015年應有規模將達到50萬輛,到2020年將達到500萬輛。據專家預測,中國十年內將有望成為最大的新能源車市場。為適應電動汽車發展要求,國家將在“十二五”期間,建設充換電站2351座,充電樁22萬股,初步建成覆蓋公司經營區域的智慧充換電服務網路。與此同時,行業在積累了一些寶貴的經驗的同時也出現了在技術路徑、政策導向、基礎設施、商業模式、行業標準等方面急需解決的一些突出問題。

        比亞迪,奇瑞,大眾,通用,富豪等車企都在為滿足新一輪中國客戶對新能源汽車的需求謀劃新局,為更好的享受政府補貼而將新能源汽車技術和生產實現本土化。

      由全國電力技術市場協會機構指導,OPPLAND MEDIA策劃組織的第二屆新能源車與充電技術中國國際論壇將於2014年4月29-30日在上海舉辦。大會網站:

        第二屆新能源車與充電技術中國國際論壇2014旨在加快新能源車推廣,促進中國電動車產業發展,進一步加強行業交流,深入探討技術創新,共同分享中國現有電動車示範城市專案運營經驗,為中國新能源車發展及環境治理出一份力。

        論壇將圍繞“清除環境與政策上的霧霾”為主題,就新能源汽車關鍵技術發展與應用,產業創新研發機制,示範運營與市場推廣、基礎配套設施建設、國際合作與交流等一系列熱點問題展開廣泛地探討與合作,推動新能源汽車產業化、市場化、國際化。本次論壇將邀請國家發展改革委等國家有關部門領導,歐洲汽車工業協會,美國加州大學、美國電動車聯盟等國際組織、中國汽車工業協會、中國汽車工程學會、中國汽車技術研究中心、清華大學、同濟大學等行業組織和研究機構的專家學者,美國加州,挪威、倫敦、北京、上海、天津、廣州、深圳等中外地方政府官員,以及國內外各大汽車集團高層,共同圍繞節能與新能源汽車產業發展的宏觀視角、政策與管理、戰略目標與方向、汽車企業的節能與新能源戰略與行動、新能源汽車發展中的新競爭以及商業探索與經驗交流等議題進行深入研討和交流。

    會議板塊:

    1. 中國新能源車政策和市場發展
    2. 建立中國新能源車標準化體系規範之路
    3. 新能源車電池和電機控制系統科技創新
    4. 中國電動車充電基礎設施建設新進展

    會議熱點:
    中國發展新能源車市場應用的路線圖
    中國新能源車市場的投資計畫以及帶給全球OEM的機會
    中國新能源車財政補貼從地方政府到車企的政策更新
    下一輪新能源車推廣應用項目概覽
    純電動車,混動車等新能源車技術交流
    下一代新能源車新電池組件和關鍵技術
    中國充電基礎設施和車企之間的協調發展
    BMS電池管理系統迎合中國新能源車增長的精細化管理
    新能源車快速發展的產業需求和規劃
    統一存在于車企、政府和供應商之間的不同標準

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※超省錢租車方案

    ※教你寫出一流的銷售文案?

    網頁設計最專業,超強功能平台可客製化

  • 一對多分頁的SQL到底應該怎麼寫?

    一對多分頁的SQL到底應該怎麼寫?

    1. 前言

    MySQL一對多的數據分頁是非常常見的需求,比如我們要查詢商品和商品的圖片信息。但是很多人會在這裏遇到分頁的誤區,得到不正確的結果。今天就來分析並解決這個問題。

    2. 問題分析

    我們先創建一個簡單商品表和對應的商品圖片關係表,它們之間是一對多的關係:

    然後我分別寫入了一些商品和這些商品對應的圖片,通過下面的左連接查詢可以看出它們之間具有明顯的一對多關係:

    SELECT P.PRODUCT_ID, P.PROD_NAME, PI.IMAGE_URL
    FROM PRODUCT_INFO P
             LEFT JOIN PRODUCT_IMAGE PI
                       ON P.PRODUCT_ID = PI.PRODUCT_ID
    

    按照傳統的思維我們的分頁語句會這麼寫:

        <resultMap id="ProductDTO" type="cn.felord.mybatis.entity.ProductDTO">
            <id property="productId" column="product_id"/>
            <result property="prodName" column="prod_name"/>
            <collection property="imageUrls"  ofType="string">
                <result column="image_url"/>
            </collection>
        </resultMap>
    
        <select id="page" resultMap="ProductDTO">
            SELECT P.PRODUCT_ID, P.PROD_NAME,PI.IMAGE_URL
            FROM PRODUCT_INFO P
                     LEFT JOIN PRODUCT_IMAGE PI
                               ON P.PRODUCT_ID = PI.PRODUCT_ID
            LIMIT #{current},#{size}
        </select>               
    

    當我按照預想傳入了(0,2)想拿到前兩個產品的數據,結果並不是我期望的:

    2020-06-21 23:35:54.515 DEBUG 10980 --- [main] c.f.m.mappers.ProductInfoMapper.page     : ==>  Preparing: SELECT P.PRODUCT_ID, P.PROD_NAME,PI.IMAGE_URL FROM PRODUCT_INFO P LEFT JOIN PRODUCT_IMAGE PI ON P.PRODUCT_ID = PI.PRODUCT_ID limit ?,? 
    2020-06-21 23:35:54.541 DEBUG 10980 --- [main] c.f.m.mappers.ProductInfoMapper.page     : ==> Parameters: 0(Long), 2(Long)
    2020-06-21 23:35:54.565 DEBUG 10980 --- [main] c.f.m.mappers.ProductInfoMapper.page     : <==      Total: 2
    page = [ProductDTO{productId=1, prodName='杯子', imageUrls=[http://asset.felord.cn/cup1.png, http://asset.felord.cn/cup2.png]}]
    

    我期望的兩條數據是杯子和筆記本,但是結果卻只有一條。原來當一對多映射時結果集會按照多的一側進行輸出(期望4條數據,實際上會有7條),而前兩條展示的只會是杯子的數據(如上圖),合併后就只有一條結果了,這樣分頁就對不上了。那麼如何才能達到我們期望的分頁效果呢?

    3. 正確的方式

    正確的思路是應該先對主表進行分頁,再關聯從表進行查詢。

    拋開框架,我們的SQL應該先對產品表進行分頁查詢然後再左關聯圖片表進行查詢:

    SELECT P.PRODUCT_ID, P.PROD_NAME, PI.IMAGE_URL
    FROM (SELECT PRODUCT_ID, PROD_NAME
          FROM PRODUCT_INFO
          LIMIT #{current},#{size}) P
             LEFT JOIN PRODUCT_IMAGE PI
                       ON P.PRODUCT_ID = PI.PRODUCT_ID
    

    這種寫法的好處就是通用性強一些。但是MyBatis提供了一個相對優雅的路子,思路依然是開頭所說的思路。只不過我們需要改造上面的Mybatis XML配置:

    <resultMap id="ProductDTO" type="cn.felord.mybatis.entity.ProductDTO">
        <id property="productId" column="product_id"/>
        <result property="prodName" column="prod_name"/>
         <!-- 利用 collection 標籤提供的 select 特性 和 column   -->
        <collection property="imageUrls" ofType="string" select="selectImagesByProductId" column="product_id"/>
    </resultMap>
    <!-- 先查詢主表的分頁數據    -->
    <select id="page" resultMap="ProductDTO">
        SELECT PRODUCT_ID, PROD_NAME
        FROM PRODUCT_INFO
        LIMIT #{current},#{size}
    </select>
    <!--根據productId 查詢對應的圖片-->
    <select id="selectImagesByProductId" resultType="string">
        SELECT IMAGE_URL
        FROM PRODUCT_IMAGE
        WHERE PRODUCT_ID = #{productId}
    </select>
    

    4. 總結

    大部分情況下分頁是很容易的,但是一對多還是有一些小小的陷阱的。一旦我們了解了其中的機制,也並不難解決。當然如果你有更好的解決方案可以留言討論,集思廣益。多多關注:碼農小胖哥,獲取更多開發技巧。

    關注公眾號:Felordcn 獲取更多資訊

    個人博客:https://felord.cn

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • LeetCode 79,這道走迷宮問題為什麼不能用寬搜呢?

    LeetCode 79,這道走迷宮問題為什麼不能用寬搜呢?

    本文始發於個人公眾號:TechFlow,原創不易,求個關注

    今天是LeetCode專題第48篇文章,我們一起來看看LeetCode當中的第79題,搜索單詞(Word Search)。

    這一題官方給的難度是Medium,通過率是34.5%,點贊3488,反對170。單從這份數據上來看,這題的質量很高,並且難度比之前的題目稍稍大一些。我個人覺得通過率是比官方給的題目難得更有參考意義的指標,10%到20%可以認為是較難的題,30%左右是偏難的題。50%是偏易題,所以如果看到某題標着Hard,但是通過率有50%,要麼說明題目很水,要麼說明數據很水,總有一點很水。

    題意

    廢話不多說,我們來看題意:

    這題的題面挺有意思,給定一個二維的字符型數組,以及一個字符串,要求我們來判斷能否在二維數組當中找到一條路徑,使得這條路徑上的字符連成的字符串和給定的字符串相等?

    樣例

    board =
    [
      ['A','B','C','E'],
      ['S','F','C','S'],
      ['A','D','E','E']
    ]
    
    Given word = "ABCCED", return true.
    Given word = "SEE", return true.
    Given word = "ABCB", return false.
    

    比如第一個字符串ABCCED,我們可以在數組當中找到這樣一條路徑:

    題解

    不知道大家看到題面和這個樣例有什麼樣的感覺,如果你刷過許多題,經常思考的話,我想應該不難發現,這道題的本質其實和走迷宮問題是一樣的。

    我們拿到的這個二維的字符型數組就是一個迷宮, 我們是要在這個迷宮當中找一條“出路”。不過我們的目的不是找到終點,而是找到一條符合題意的路徑。在走迷宮問題當中,迷宮中不是每一個點都可以走的,同樣在當前問題當中,也不是每一個點都符合字符串的要求的。這兩個問題雖然題面看起來大相徑庭,但是核心的本質是一樣的。

    我們來回憶一下,走迷宮問題應該怎麼解決?

    這個答案應該已經非常確定了,當然是搜索算法。我們需要搜索解可能存在的空間去尋找存在的解,也就是說我們面臨的是一個解是否存在的問題,要麼找到解,要麼遍歷完所有的可能性發現解不存在。確定了是搜索算法之後,剩下的就簡單了,我們只有兩個選項,深度優先或者是廣度優先。

    理論上來說,一般判斷解的存在性問題,我們使用廣度優先搜索更多,因為一般來說它可以更快地找到解。但是本題當中有一個小問題是,廣度優先搜索需要在隊列當中存儲中間狀態,需要記錄地圖上行走過的信息,每有一個狀態就需要存儲一份地圖信息,這會帶來比較大的內存開銷,同樣存儲的過程也會帶來計算開銷,在這道題當中,這是不可以接受的。拷貝狀態帶來的空間消耗還是小事,關鍵是拷貝帶來的時間開銷,就足夠讓這題超時了。所以我們別無選擇,只能深度優先。

    明確了算法之後,只剩下了最後一個問題,在這個走迷宮問題當中,我們怎麼找到迷宮的入口呢?因為題目當中並沒有規定我們起始點的位置,這也不難解決,我們遍歷二維的字符數組,和字符串開頭相匹配的位置都可以作為迷宮的入口。

    最後,我們來看代碼,並沒有什麼技術含量,只是簡單的回溯法而已。

    class Solution:
        def exist(self, board: List[List[str]], word: str) -> bool:
            fx = [[0, 1], [0, -1], [1, 0], [-1, 0]]
            def dfs(x, y, l):
                if l == len(word):
                    return True
                for i in range(4):
                    nx = x + fx[i][0]
                    ny = y + fx[i][1]
                    # 出界或者是走過的時候,跳過
                    if nx < 0 or nx == n or ny < 0 or ny == m or visited[nx][ny]:
                        continue
                    if board[nx][ny] == word[l]:
                        visited[nx][ny] = 1
                        if dfs(nx, ny, l+1):
                            return True
                        visited[nx][ny] = 0
                return False
                    
            n = len(board)
            if n == 0:
                return False
            m = len(board[0])
            if m == 0:
                return False
            
            visited = [[0 for i in range(m)] for j in range(n)]
            
            for i in range(n):
                for j in range(m):
                    # 找到合法的起點
                    if board[i][j] == word[0]:
                        visited = [[0 for _ in range(m)] for _ in range(n)]
                        visited[i][j] = 1
                        if dfs(i, j, 1):
                            return True
                        
            return False
    

    總結

    如果能夠想通回溯法,並且對於回溯法的實現足夠熟悉,那麼這題的難度是不大的。實際上至今為止,我們一路刷來,已經做了好幾道回溯法的問題了,我想對你們來說,回溯法的問題應該已經小菜一碟了。

    相比於回溯法來說,我覺得更重要的是我們能夠通過分析想清楚,為什麼廣度優先搜索不行,底層核心的本質原因是什麼。這個思考的過程往往比最後的結論來得重要。

    如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

    本文使用 mdnice 排版

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案