當前位置: 首頁>>代碼示例>>Golang>>正文


Golang config.CgrConfig函數代碼示例

本文整理匯總了Golang中github.com/cgrates/cgrates/config.CgrConfig函數的典型用法代碼示例。如果您正苦於以下問題:Golang CgrConfig函數的具體用法?Golang CgrConfig怎麽用?Golang CgrConfig使用的例子?那麽, 這裏精選的函數代碼示例或許可以為您提供幫助。


在下文中一共展示了CgrConfig函數的15個代碼示例,這些例子默認根據受歡迎程度排序。您可以為喜歡或者感覺有用的代碼點讚,您的評價將有助於係統推薦出更棒的Golang代碼示例。

示例1: GetExtraFields

func (fsev FSEvent) GetExtraFields() map[string]string {
	extraFields := make(map[string]string)
	for _, fldRule := range config.CgrConfig().SmFsConfig.ExtraFields {
		extraFields[fldRule.Id] = fsev.ParseEventValue(fldRule, config.CgrConfig().DefaultTimezone)
	}
	return extraFields
}
開發者ID:kevinlovesing,項目名稱:cgrates,代碼行數:7,代碼來源:fsevent.go

示例2: MissingParameter

func (kev KamEvent) MissingParameter() bool {
	var nullTime time.Time
	switch kev.GetName() {
	case CGR_AUTH_REQUEST:
		if setupTime, err := kev.GetSetupTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone); err != nil || setupTime == nullTime {
			return true
		}
		return len(kev.GetAccount(utils.META_DEFAULT)) == 0 ||
			len(kev.GetDestination(utils.META_DEFAULT)) == 0 ||
			len(kev[KAM_TR_INDEX]) == 0 || len(kev[KAM_TR_LABEL]) == 0
	case CGR_LCR_REQUEST:
		return len(kev.GetAccount(utils.META_DEFAULT)) == 0 ||
			len(kev.GetDestination(utils.META_DEFAULT)) == 0 ||
			len(kev[KAM_TR_INDEX]) == 0 || len(kev[KAM_TR_LABEL]) == 0
	case CGR_CALL_START:
		if aTime, err := kev.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone); err != nil || aTime == nullTime {
			return true
		}
		return len(kev.GetUUID()) == 0 ||
			len(kev.GetAccount(utils.META_DEFAULT)) == 0 ||
			len(kev.GetDestination(utils.META_DEFAULT)) == 0 ||
			len(kev[HASH_ENTRY]) == 0 || len(kev[HASH_ID]) == 0
	case CGR_CALL_END:
		return len(kev.GetUUID()) == 0 ||
			len(kev.GetAccount(utils.META_DEFAULT)) == 0 ||
			len(kev.GetDestination(utils.META_DEFAULT)) == 0 ||
			len(kev[CGR_DURATION]) == 0
	default:
		return true
	}

}
開發者ID:nikbyte,項目名稱:cgrates,代碼行數:32,代碼來源:kamevent.go

示例3: MissingParameter

func (osipsev *OsipsEvent) MissingParameter() bool {
	var nilTime time.Time
	if osipsev.GetName() == "E_ACC_EVENT" && osipsev.osipsEvent.AttrValues["method"] == "INVITE" {
		return len(osipsev.GetUUID()) == 0 ||
			len(osipsev.GetAccount(utils.META_DEFAULT)) == 0 ||
			len(osipsev.GetDestination(utils.META_DEFAULT)) == 0 ||
			len(osipsev.osipsEvent.AttrValues[OSIPS_DIALOG_ID]) == 0
	} else if osipsev.GetName() == "E_ACC_EVENT" && osipsev.osipsEvent.AttrValues["method"] == "BYE" {
		return len(osipsev.osipsEvent.AttrValues[OSIPS_DIALOG_ID]) == 0 ||
			len(osipsev.osipsEvent.AttrValues[TIME]) == 0
	} else if osipsev.GetName() == "E_ACC_EVENT" && osipsev.osipsEvent.AttrValues["method"] == "UPDATE" { // Updated event out of start/stop
		// Data needed when stopping a prepaid loop or building a CDR with start/stop event
		setupTime, err := osipsev.GetSetupTime(TIME, config.CgrConfig().DefaultTimezone)
		if err != nil || setupTime.Equal(nilTime) {
			return true
		}
		aTime, err := osipsev.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone)
		if err != nil || aTime.Equal(nilTime) {
			return true
		}
		endTime, err := osipsev.GetEndTime()
		if err != nil || endTime.Equal(nilTime) {
			return true
		}
		_, err = osipsev.GetDuration(utils.META_DEFAULT)
		if err != nil {
			return true
		}
		if osipsev.osipsEvent.AttrValues[OSIPS_DIALOG_ID] == "" {
			return true
		}
		return false
	}
	return true
}
開發者ID:nikbyte,項目名稱:cgrates,代碼行數:35,代碼來源:osipsevent.go

示例4: GetReqType

func (fsev FSEvent) GetReqType(fieldName string) string {
	if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
		return fieldName[len(utils.STATIC_VALUE_PREFIX):]
	} else if fieldName == utils.META_DEFAULT {
		return utils.FirstNonEmpty(fsev[REQTYPE], config.CgrConfig().DefaultReqType)
	}
	return utils.FirstNonEmpty(fsev[fieldName], fsev[REQTYPE], config.CgrConfig().DefaultReqType)
}
開發者ID:intralanman,項目名稱:cgrates,代碼行數:8,代碼來源:fsevent.go

示例5: SureTaxProcessCdr

func SureTaxProcessCdr(cdr *CDR) error {
	stCfg := config.CgrConfig().SureTaxCfg()
	if stCfg == nil {
		return errors.New("Invalid SureTax configuration")
	}
	if sureTaxClient == nil { // First time used, init the client here
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: config.CgrConfig().HttpSkipTlsVerify},
		}
		sureTaxClient = &http.Client{Transport: tr}
	}
	req, err := NewSureTaxRequest(cdr, stCfg)
	if err != nil {
		return err
	}
	jsnContent, err := json.Marshal(req)
	if err != nil {
		return err
	}
	resp, err := sureTaxClient.Post(stCfg.Url, "application/json", bytes.NewBuffer(jsnContent))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode > 299 {
		return fmt.Errorf("Unexpected status code received: %d", resp.StatusCode)
	}
	var respFull SureTaxResponse
	if err := json.Unmarshal(respBody, &respFull); err != nil {
		return err
	}
	var stResp STResponse
	if err := json.Unmarshal([]byte(respFull.D), &stResp); err != nil {
		return err
	}
	if stResp.ResponseCode != "9999" {
		cdr.ExtraInfo = stResp.HeaderMessage
		return nil // No error because the request was processed by SureTax, error will be in the ExtraInfo
	}
	// Write cost to CDR
	totalTax, err := strconv.ParseFloat(stResp.TotalTax, 64)
	if err != nil {
		cdr.ExtraInfo = err.Error()
	}
	if !stCfg.IncludeLocalCost {
		cdr.Cost = utils.Round(totalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)
	} else {
		cdr.Cost = utils.Round(cdr.Cost+totalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)
	}
	// Add response into extra fields to be available for later review
	cdr.ExtraFields[utils.META_SURETAX] = respFull.D
	return nil
}
開發者ID:cgrates,項目名稱:cgrates,代碼行數:57,代碼來源:suretax.go

示例6: updateDurationFromEvent

// Computes duration out of setup time of the callEnd
func (osipsEv *OsipsEvent) updateDurationFromEvent(updatedOsipsEv *OsipsEvent) error {
	endTime, err := updatedOsipsEv.GetSetupTime(TIME, config.CgrConfig().DefaultTimezone)
	if err != nil {
		return err
	}
	answerTime, err := osipsEv.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone)
	osipsEv.osipsEvent.AttrValues[OSIPS_DURATION] = endTime.Sub(answerTime).String()
	osipsEv.osipsEvent.AttrValues["method"] = "UPDATE" // So we can know it is an end event
	osipsEv.osipsEvent.AttrValues[OSIPS_SIPCODE] = updatedOsipsEv.osipsEvent.AttrValues[OSIPS_SIPCODE]
	return nil
}
開發者ID:iwada,項目名稱:cgrates,代碼行數:12,代碼來源:osipsevent.go

示例7: SureTaxProcessCdr

func SureTaxProcessCdr(cdr *StoredCdr) error {
	stCfg := config.CgrConfig().SureTaxCfg()
	if stCfg == nil {
		return errors.New("Invalid SureTax configuration")
	}
	if sureTaxClient == nil { // First time used, init the client here
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: config.CgrConfig().HttpSkipTlsVerify},
		}
		sureTaxClient = &http.Client{Transport: tr}
	}
	req, err := NewSureTaxRequest(cdr, stCfg)
	if err != nil {
		return err
	}

	body, err := json.Marshal(req)
	if err != nil {
		return err
	}
	utils.Logger.Debug(fmt.Sprintf("###SureTax NewSureTaxRequest: %+v, ItemList: %+v\n", req, req.ItemList[0]))
	resp, err := sureTaxClient.Post(stCfg.Url, "application/json", bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode > 299 {
		return fmt.Errorf("Unexpected status code received: %d", resp.StatusCode)
	}
	var stResp SureTaxResponse
	if err := json.Unmarshal(respBody, &stResp); err != nil {
		return err
	}
	utils.Logger.Debug(fmt.Sprintf("###SureTax received response: %+v\n", stResp))
	if stResp.ResponseCode != 9999 {
		cdr.ExtraInfo = stResp.HeaderMessage
		return nil // No error because the request was processed by SureTax, error will be in the ExtraInfo
	}
	// Write cost to CDR
	if !stCfg.IncludeLocalCost {
		cdr.Cost = utils.Round(stResp.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)
	} else {
		cdr.Cost = utils.Round(cdr.Cost+stResp.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)
	}
	// Add response into extra fields to be available for later review
	cdr.ExtraFields[utils.META_SURETAX] = string(respBody)
	return nil
}
開發者ID:henrylee2cn,項目名稱:cgrates,代碼行數:52,代碼來源:suretax.go

示例8: ParseEventValue

// Useful for CDR generation
func (kev KamEvent) ParseEventValue(rsrFld *utils.RSRField, timezone string) string {
	sTime, _ := kev.GetSetupTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone)
	aTime, _ := kev.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone)
	duration, _ := kev.GetDuration(utils.META_DEFAULT)
	switch rsrFld.Id {
	case utils.CGRID:
		return rsrFld.ParseValue(kev.GetCgrId(timezone))
	case utils.TOR:
		return rsrFld.ParseValue(utils.VOICE)
	case utils.ACCID:
		return rsrFld.ParseValue(kev.GetUUID())
	case utils.CDRHOST:
		return rsrFld.ParseValue(kev.GetOriginatorIP(utils.META_DEFAULT))
	case utils.CDRSOURCE:
		return rsrFld.ParseValue(kev.GetCdrSource())
	case utils.REQTYPE:
		return rsrFld.ParseValue(kev.GetReqType(utils.META_DEFAULT))
	case utils.DIRECTION:
		return rsrFld.ParseValue(kev.GetDirection(utils.META_DEFAULT))
	case utils.TENANT:
		return rsrFld.ParseValue(kev.GetTenant(utils.META_DEFAULT))
	case utils.CATEGORY:
		return rsrFld.ParseValue(kev.GetCategory(utils.META_DEFAULT))
	case utils.ACCOUNT:
		return rsrFld.ParseValue(kev.GetAccount(utils.META_DEFAULT))
	case utils.SUBJECT:
		return rsrFld.ParseValue(kev.GetSubject(utils.META_DEFAULT))
	case utils.DESTINATION:
		return rsrFld.ParseValue(kev.GetDestination(utils.META_DEFAULT))
	case utils.SETUP_TIME:
		return rsrFld.ParseValue(sTime.String())
	case utils.ANSWER_TIME:
		return rsrFld.ParseValue(aTime.String())
	case utils.USAGE:
		return rsrFld.ParseValue(strconv.FormatFloat(utils.Round(duration.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64))
	case utils.PDD:
		return rsrFld.ParseValue(strconv.FormatFloat(utils.Round(duration.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64))
	case utils.SUPPLIER:
		return rsrFld.ParseValue(kev.GetSupplier(utils.META_DEFAULT))
	case utils.DISCONNECT_CAUSE:
		return rsrFld.ParseValue(kev.GetDisconnectCause(utils.META_DEFAULT))
	case utils.MEDI_RUNID:
		return rsrFld.ParseValue(utils.META_DEFAULT)
	case utils.COST:
		return rsrFld.ParseValue("-1.0")
	default:
		return rsrFld.ParseValue(kev.GetExtraFields()[rsrFld.Id])
	}
}
開發者ID:bhepp,項目名稱:cgrates,代碼行數:50,代碼來源:kamevent.go

示例9: AsCallDescriptor

func (self *LcrRequest) AsCallDescriptor(timezone string) (*CallDescriptor, error) {
	if len(self.Account) == 0 || len(self.Destination) == 0 {
		return nil, utils.ErrMandatoryIeMissing
	}
	// Set defaults
	if len(self.Direction) == 0 {
		self.Direction = utils.OUT
	}
	if len(self.Tenant) == 0 {
		self.Tenant = config.CgrConfig().DefaultTenant
	}
	if len(self.Category) == 0 {
		self.Category = config.CgrConfig().DefaultCategory
	}
	if len(self.Subject) == 0 {
		self.Subject = self.Account
	}
	var timeStart time.Time
	var err error
	if len(self.SetupTime) == 0 {
		timeStart = time.Now()
	} else if timeStart, err = utils.ParseTimeDetectLayout(self.SetupTime, timezone); err != nil {
		return nil, err
	}
	var callDur time.Duration
	if len(self.Duration) == 0 {
		callDur = time.Duration(1) * time.Minute
	} else if callDur, err = utils.ParseDurationWithSecs(self.Duration); err != nil {
		return nil, err
	}
	cd := &CallDescriptor{
		Direction:   self.Direction,
		Tenant:      self.Tenant,
		Category:    self.Category,
		Account:     self.Account,
		Subject:     self.Subject,
		Destination: self.Destination,
		TimeStart:   timeStart,
		TimeEnd:     timeStart.Add(callDur),
	}
	if self.ExtraFields != nil {
		cd.ExtraFields = make(map[string]string)
	}
	for key, val := range self.ExtraFields {
		cd.ExtraFields[key] = val
	}
	return cd, nil
}
開發者ID:iwada,項目名稱:cgrates,代碼行數:48,代碼來源:lcr.go

示例10: onChannelHangupComplete

func (sm *FSSessionManager) onChannelHangupComplete(ev engine.Event) {
	if ev.GetReqType(utils.META_DEFAULT) == utils.META_NONE { // Do not process this request
		return
	}
	var s *Session
	for i := 0; i < 2; i++ { // Protect us against concurrency, wait a couple of seconds for the answer to be populated before we process hangup
		s = sm.sessions.getSession(ev.GetUUID())
		if s != nil {
			break
		}
		time.Sleep(time.Duration(i+1) * time.Second)
	}
	if s != nil { // Handled by us, cleanup here
		if err := sm.sessions.removeSession(s, ev); err != nil {
			utils.Logger.Err(err.Error())
		}
	}
	if sm.cfg.CreateCdr {
		sm.ProcessCdr(ev.AsStoredCdr(config.CgrConfig().DefaultTimezone))
	}
	var reply string
	attrRU := utils.AttrRLsResourceUsage{
		ResourceUsageID: ev.GetUUID(),
		Event:           ev.(FSEvent).AsMapStringInterface(sm.timezone),
		RequestedUnits:  1,
	}
	if sm.rls != nil {
		if err := sm.rls.Call("RLsV1.TerminateResourceUsage", attrRU, &reply); err != nil {
			utils.Logger.Err(fmt.Sprintf("<SM-FreeSWITCH> RLs API error: %s", err.Error()))
		}
	}
}
開發者ID:eloycoto,項目名稱:cgrates,代碼行數:32,代碼來源:fssessionmanager.go

示例11: GetExtraFields

func (fsev FSEvent) GetExtraFields() map[string]string {
	extraFields := make(map[string]string)
	for _, fldRule := range config.CgrConfig().FSCdrExtraFields {
		extraFields[fldRule.Id] = fsev.ParseEventValue(fldRule)
	}
	return extraFields
}
開發者ID:intralanman,項目名稱:cgrates,代碼行數:7,代碼來源:fsevent.go

示例12: callUrlAsync

// Does not block for posts, no error reports
func callUrlAsync(ub *Account, sq *StatsQueueTriggered, a *Action) error {
	var o interface{}
	if ub != nil {
		o = ub
	}
	if sq != nil {
		o = sq
	}
	jsn, err := json.Marshal(o)
	if err != nil {
		return err
	}
	cfg := config.CgrConfig()
	go func() {
		for i := 0; i < 5; i++ { // Loop so we can increase the success rate on best effort
			if _, err = utils.HttpJsonPost(a.ExtraParameters, cfg.HttpSkipTlsVerify, o); err == nil {
				break // Success, no need to reinterate
			} else if i == 4 { // Last iteration, syslog the warning
				Logger.Warning(fmt.Sprintf("<Triggers> WARNING: Failed calling url: [%s], error: [%s], triggered: %s", a.ExtraParameters, err.Error(), jsn))
				break
			}
			time.Sleep(time.Duration(i) * time.Minute)
		}

	}()
	return nil
}
開發者ID:intralanman,項目名稱:cgrates,代碼行數:28,代碼來源:action.go

示例13: setCgrLcr

// Queries LCR and sets the cgr_lcr channel variable
func (sm *FSSessionManager) setCgrLcr(ev engine.Event, connId string) error {
	var lcrCost engine.LCRCost
	startTime, err := ev.GetSetupTime(utils.META_DEFAULT, sm.timezone)
	if err != nil {
		return err
	}
	cd := &engine.CallDescriptor{
		Direction:   ev.GetDirection(utils.META_DEFAULT),
		Tenant:      ev.GetTenant(utils.META_DEFAULT),
		Category:    ev.GetCategory(utils.META_DEFAULT),
		Subject:     ev.GetSubject(utils.META_DEFAULT),
		Account:     ev.GetAccount(utils.META_DEFAULT),
		Destination: ev.GetDestination(utils.META_DEFAULT),
		TimeStart:   startTime,
		TimeEnd:     startTime.Add(config.CgrConfig().MaxCallDuration),
	}
	if err := sm.rater.GetLCR(&engine.AttrGetLcr{CallDescriptor: cd}, &lcrCost); err != nil {
		return err
	}
	supps := []string{}
	for _, supplCost := range lcrCost.SupplierCosts {
		if dtcs, err := utils.NewDTCSFromRPKey(supplCost.Supplier); err != nil {
			return err
		} else if len(dtcs.Subject) != 0 {
			supps = append(supps, dtcs.Subject)
		}
	}
	fsArray := SliceAsFsArray(supps)
	if _, err = sm.conns[connId].SendApiCmd(fmt.Sprintf("uuid_setvar %s cgr_notify %s\n\n", ev.GetUUID(), fsArray)); err != nil {
		return err
	}
	return nil
}
開發者ID:henrylee2cn,項目名稱:cgrates,代碼行數:34,代碼來源:fssessionmanager.go

示例14: callUrlAsync

// Does not block for posts, no error reports
func callUrlAsync(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
	var o interface{}
	if ub != nil {
		o = ub
	}
	if sq != nil {
		o = sq
	}
	jsn, err := json.Marshal(o)
	if err != nil {
		return err
	}
	cfg := config.CgrConfig()
	fallbackPath := path.Join(cfg.HttpFailedDir, fmt.Sprintf("act_%s_%s_%s.json", a.ActionType, a.ExtraParameters, utils.GenUUID()))
	go utils.NewHTTPPoster(config.CgrConfig().HttpSkipTlsVerify,
		config.CgrConfig().ReplyTimeout).Post(a.ExtraParameters, utils.CONTENT_JSON, jsn, config.CgrConfig().HttpPosterAttempts, fallbackPath)
	return nil
}
開發者ID:cgrates,項目名稱:cgrates,代碼行數:19,代碼來源:action.go

示例15: cgrRPCAction

/*
<< .Object.Property >>

Property can be a attribute or a method both used without ()
Please also note the initial dot .

Currently there are following objects that can be used:

Account -  the account that this action is called on
Action - the action with all it's attributs
Actions - the list of actions in the current action set
Sq - StatsQueueTriggered object

We can actually use everythiong that go templates offer. You can read more here: https://golang.org/pkg/text/template/
*/
func cgrRPCAction(account *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
	// parse template
	tmpl := template.New("extra_params")
	tmpl.Delims("<<", ">>")
	t, err := tmpl.Parse(a.ExtraParameters)
	if err != nil {
		utils.Logger.Err(fmt.Sprintf("error parsing *cgr_rpc template: %s", err.Error()))
		return err
	}
	var buf bytes.Buffer
	if err = t.Execute(&buf, struct {
		Account *Account
		Sq      *StatsQueueTriggered
		Action  *Action
		Actions Actions
	}{account, sq, a, acs}); err != nil {
		utils.Logger.Err(fmt.Sprintf("error executing *cgr_rpc template %s:", err.Error()))
		return err
	}
	processedExtraParam := buf.String()
	//utils.Logger.Info("ExtraParameters: " + parsedExtraParameters)
	req := RPCRequest{}
	if err := json.Unmarshal([]byte(processedExtraParam), &req); err != nil {
		return err
	}
	params, err := utils.GetRpcParams(req.Method)
	if err != nil {
		return err
	}
	var client rpcclient.RpcClientConnection
	if req.Address != utils.MetaInternal {
		if client, err = rpcclient.NewRpcClient("tcp", req.Address, req.Attempts, 0, config.CgrConfig().ConnectTimeout, config.CgrConfig().ReplyTimeout, req.Transport, nil); err != nil {
			return err
		}
	} else {
		client = params.Object.(rpcclient.RpcClientConnection)
	}
	in, out := params.InParam, params.OutParam
	//utils.Logger.Info("Params: " + utils.ToJSON(req.Params))
	//p, err := utils.FromMapStringInterfaceValue(req.Params, in)
	mapstructure.Decode(req.Params, in)
	if err != nil {
		utils.Logger.Info("<*cgr_rpc> err: " + err.Error())
		return err
	}
	utils.Logger.Info(fmt.Sprintf("<*cgr_rpc> calling: %s with: %s", req.Method, utils.ToJSON(in)))
	if !req.Async {
		err = client.Call(req.Method, in, out)
		utils.Logger.Info(fmt.Sprintf("<*cgr_rpc> result: %s err: %v", utils.ToJSON(out), err))
		return err
	}
	go func() {
		err := client.Call(req.Method, in, out)
		utils.Logger.Info(fmt.Sprintf("<*cgr_rpc> result: %s err: %v", utils.ToJSON(out), err))
	}()
	return nil
}
開發者ID:iwada,項目名稱:cgrates,代碼行數:72,代碼來源:action.go


注:本文中的github.com/cgrates/cgrates/config.CgrConfig函數示例由純淨天空整理自Github/MSDocs等開源代碼及文檔管理平台,相關代碼片段篩選自各路編程大神貢獻的開源項目,源碼版權歸原作者所有,傳播和使用請參考對應項目的License;未經允許,請勿轉載。