自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

游戲萬能卡片- Serverless—端云一體化開發(fā)

系統(tǒng) OpenHarmony
為豐富HarmonyOS對云端開發(fā)的支持、實現(xiàn)HarmonyOS生態(tài)端云聯(lián)動,DevEco Studio推出了云開發(fā)功能,開發(fā)者在創(chuàng)建工程時選擇云開發(fā)模板,即可在DevEco Studio內(nèi)同時完成HarmonyOS應(yīng)用/服務(wù)的端側(cè)與云側(cè)開發(fā),體驗端云一體化協(xié)同開發(fā)。

想了解更多關(guān)于開源的內(nèi)容,請訪問:

51CTO 開源基礎(chǔ)軟件社區(qū)

https://ost.51cto.com

1、前言

舒爾特方格游戲-關(guān)系型數(shù)據(jù)庫版本,請移步到上一篇帖子查看,本篇帖子內(nèi)容還是舒爾特方格游戲,只是換了后宮_云數(shù)據(jù)庫,此項目是用最新版DevEco Studio 3.1 Release并創(chuàng)建端云一體開發(fā),由于目前此版本不支持直接調(diào)用云數(shù)據(jù)庫,不過可以通過云函數(shù)調(diào)用云數(shù)據(jù)庫,也就是在服務(wù)卡片業(yè)務(wù)邏輯里通過調(diào)用云函數(shù)來完成游戲數(shù)據(jù)保存到云數(shù)據(jù)庫,開發(fā)工具支持本地函數(shù)調(diào)用測試,大大方便了開發(fā),此貼重點講解云函數(shù)和云數(shù)據(jù)庫開發(fā),本地和遠端調(diào)用,從而進一步學(xué)習(xí)Serverless知識,舒爾特方格游戲效果圖如下:

游戲萬能卡片- Serverless(端云一體化開發(fā))-開源基礎(chǔ)軟件社區(qū)

2、知識點

為豐富HarmonyOS對云端開發(fā)的支持、實現(xiàn)HarmonyOS生態(tài)端云聯(lián)動,DevEco Studio推出了云開發(fā)功能,開發(fā)者在創(chuàng)建工程時選擇云開發(fā)模板,即可在DevEco Studio內(nèi)同時完成HarmonyOS應(yīng)用/服務(wù)的端側(cè)與云側(cè)開發(fā),體驗端云一體化協(xié)同開發(fā)。

相比于傳統(tǒng)開發(fā)模式,云開發(fā)模式具備成本低、效率高、門檻低等優(yōu)勢,具體區(qū)別見下表。

區(qū)別點

傳統(tǒng)開發(fā)模式

云開發(fā)模式

開發(fā)工具

端側(cè)與云側(cè)各需一套開發(fā)工具,云側(cè)需自建服務(wù)器,工具成本高。

DevEco Studio一套開發(fā)工具即可支撐端側(cè)與云側(cè)同時開發(fā),無需搭建服務(wù)器,工具成本低。

開發(fā)人員

端側(cè)與云側(cè)要求不同的開發(fā)語言,技能要求高。需多人投入,且開發(fā)人員之間需持續(xù)、準(zhǔn)確溝通,人力與溝通成本高、效率低。

依托AppGallery Connect(以下簡稱AGC)Serverless云服務(wù)開放的接口,端側(cè)開發(fā)人員也能輕松開發(fā)云側(cè)代碼,大大降低開發(fā)門檻。開發(fā)人員數(shù)量少,降低人力成本,提高溝通效率。

運維

需自行構(gòu)建運營與運維能力,成本高、負(fù)擔(dān)重。

直接接入AGC Serverless云服務(wù),實現(xiàn)免運維,無運維成本或資源浪費。

(1)開發(fā)流程

HarmonyOS應(yīng)用端云一體化開發(fā)流程如下圖所示。

游戲萬能卡片- Serverless(端云一體化開發(fā))-開源基礎(chǔ)軟件社區(qū)

(2)創(chuàng)建端云一體化開發(fā)工程

 新建原子化服務(wù)工程

 工程初始化配置

 端云一體化開發(fā)工程介紹

(3)開發(fā)云工程

 開發(fā)云函數(shù)

 開發(fā)云數(shù)據(jù)庫

(4)部署云工程

 部署云工程

(5)小結(jié)

      了解這些端云一體化開發(fā)知識點后,下面圍繞舒爾特方格游戲,在云數(shù)據(jù)庫里設(shè)計卡片表結(jié)構(gòu)和成績表結(jié)構(gòu),然后再編寫相關(guān)云函數(shù),并在本地測試通過后,再測試遠程,最后在元服務(wù)業(yè)務(wù)邏輯調(diào)用云函數(shù)。

3、云數(shù)據(jù)庫開發(fā)講解

(1)objecttype創(chuàng)建

展開CloudProgram -> clouddb -> objecttype 右擊objecttype目錄,創(chuàng)建 -> Cloud DB Object Type 輸入Object Type Name為t_form,點擊確認(rèn),修改內(nèi)容如下:

{
  "fields": [
    {
      "isNeedEncrypt": false,
      "fieldName": "formId",
      "notNull": true,
      "belongPrimaryKey": true,
      "fieldType": "String"
    },
    {
      "isNeedEncrypt": false,
      "fieldName": "formName",
      "notNull": true,
      "defaultValue": "",
      "belongPrimaryKey": false,
      "fieldType": "String"
    },
    {
      "isNeedEncrypt": false,
      "fieldName": "dimension",
      "notNull": true,
      "defaultValue": "0",
      "belongPrimaryKey": false,
      "fieldType": "Integer"
    }
  ],
  "indexes": [
    {
      "indexName": "formId",
      "indexList": [{ "fieldName": "formId", "sortType": "ASC" }]
    }
  ],
  "objectTypeName": "t_form",
  "permissions": [...]
}

展開CloudProgram -> clouddb -> objecttype 右擊objecttype目錄,創(chuàng)建 -> Cloud DB Object Type 輸入Object Type Name為t_score,點擊確認(rèn),修改內(nèi)容如下:

{
  "fields": [
    {
      "isNeedEncrypt": false,
      "fieldName": "formId",
      "notNull": true,
      "belongPrimaryKey": true,
      "fieldType": "String"
    },
    {
      "isNeedEncrypt": false,
      "fieldName": "matrixNum",
      "notNull": true,
      "defaultValue": "",
      "belongPrimaryKey": false,
      "fieldType": "String"
    },
    {
      "isNeedEncrypt": false,
      "fieldName": "bestScore",
      "notNull": true,
      "defaultValue": "0",
      "belongPrimaryKey": false,
      "fieldType": "Double"
    }
  ],
  "indexes": [
    {
      "indexName": "formId",
      "indexList": [{ "fieldName": "formId", "sortType": "ASC" }]
    }
  ],
  "objectTypeName": "t_score",
  "permissions": [...]
}

(2)dataentry創(chuàng)建

展開CloudProgram -> clouddb -> dataentry 右擊dataentry目錄,創(chuàng)建 -> Cloud DB Data Entry 這里先選擇上面創(chuàng)建的Object Type為t_form,再輸入Data Entry Name為form_data,點擊確認(rèn),修改內(nèi)容如下:

{
  "cloudDBZoneName": "widgetCard",
  "objectTypeName": "t_form",
  "objects": [
    {
      "formId": "x000001",
      "formName": "卡片1",
      "dimension": 2
    }
  ]
}

展開CloudProgram -> clouddb -> dataentry 右擊dataentry目錄,創(chuàng)建 -> Cloud DB Data Entry 這里先選擇上面創(chuàng)建的Object Type為t_score,再輸入Data Entry Name為score_data,點擊確認(rèn),修改內(nèi)容如下:

{
  "cloudDBZoneName": "widgetCard",
  "objectTypeName": "t_score",
  "objects": [
    {
      "formId": "x000001",
      "matrixNum": "3x3",
      "bestScore": 2.234
    }
  ]
}

(3)小結(jié)

其實dataentry文件可以不創(chuàng)建,這里對兩個表都初始化了一條數(shù)據(jù),是方便下面的調(diào)用使用,云數(shù)據(jù)庫就是定義好表結(jié)構(gòu)、權(quán)限配置就可以,數(shù)據(jù)的添加、修改、刪除、查詢都可以通過云函數(shù)來完成。

4、云函數(shù)開發(fā)講解

(1)卡片云函數(shù)創(chuàng)建

展開CloudProgram -> cloudfunctions 右擊cloudfunctions目錄,創(chuàng)建 -> Cloud Function 輸入Cloud Function Name為form-func,點擊確認(rèn), 卡片云函數(shù)里包含了增刪改查操作,所以在form-func下,創(chuàng)建不同的文件夾來區(qū)分,目錄結(jié)構(gòu)如下:

游戲萬能卡片- Serverless(端云一體化開發(fā))-開源基礎(chǔ)軟件社區(qū)

首先說一下與云數(shù)據(jù)庫交互文件,t_form.js對應(yīng)的是云數(shù)據(jù)庫實體類,如各屬性的get和set方法,之前FA模式下的DevEco Studio端云一體化開發(fā),支持直接調(diào)用云數(shù)據(jù)庫,現(xiàn)在Stage模式下的DevEco Studio端云一體化開發(fā),還不支持直接調(diào)用云數(shù)據(jù)庫,通過云函數(shù)來調(diào)用,所以這里的云數(shù)據(jù)庫實體類,除了屬性的get和set方法外,還要手工添加一些方法,如卡片實例體類:

class t_form {
    getFieldTypeMap() {
        let fieldTypeMap = new Map();
        fieldTypeMap.set('formId', 'String');
        fieldTypeMap.set('formName', 'String');
        fieldTypeMap.set('dimension', 'Integer');
        return fieldTypeMap;
    }
    
    getClassName() {
        return 't_form';
    }

    getPrimaryKeyList() {
        let primaryKeyList = [];
        primaryKeyList.push('formId');
        return primaryKeyList;
    }

    getIndexList() {
        let indexList = [];
        return indexList;
    }

    getEncryptedFieldList() {
        let encryptedFieldList = [];
        return encryptedFieldList;
    }

	// set and get
    setFormId(formId) {this.formId = formId;}
    getFormId() {return this.formId;}
    setFormName(formName) {this.formName = formName;}
    getFormName() {return this.formName;}
    setDimension(dimension) {this.dimension = dimension;}
    getDimension() {return this.dimension;}
}

module.exports = {t_form}

CloudDBZoneWrapper操作云數(shù)據(jù)庫,這里主要列舉構(gòu)造函數(shù)和增加方法內(nèi)容:

import * as clouddb from '@agconnect/database-server';
import { t_form as FormBean } from './models/t_form';
import * as agconnect from '@agconnect/common-server';

const ZONE_NAME = "widgetCard";

export class CloudDBZoneWrapper {
  logger;
  cloudDbZone;

  constructor(credential, logger) {
    this.logger = logger;
    try {
      // 初始化AGCClient
      let agcClient;
      try {
        agcClient = agconnect.AGCClient.getInstance();
      } catch {
        agconnect.AGCClient.initialize(credential);
        agcClient = agconnect.AGCClient.getInstance();
      }
      // 初始化AGConnectCloudDB實例
      let cloudDbInstance;
      try {
        cloudDbInstance = clouddb.AGConnectCloudDB.getInstance(agcClient);
      } catch {
        clouddb.AGConnectCloudDB.initialize(agcClient);
        cloudDbInstance = clouddb.AGConnectCloudDB.getInstance(agcClient);
      }
      // 創(chuàng)建CloudDBZoneConfig配置對象,并設(shè)置云側(cè)CloudDB zone名稱,打開Cloud DB zone實例
      const cloudDBZoneConfig = new clouddb.CloudDBZoneConfig(ZONE_NAME);
      this.cloudDbZone = cloudDbInstance.openCloudDBZone(cloudDBZoneConfig);
    } catch (err) {
      logger.error("xx [form-func]CloudDBZoneWrapper init CloudDBZoneWrapper error: " + err);
    }
  }

  async insert(addForm) {
    if (!this.cloudDbZone) {
      this.logger.error("xx  [form-func]CloudDBZoneWrapper->insert CloudDBClient is null, try re-initialize it");
    }

    try {
      let res = await this.cloudDbZone.executeUpsert(addForm);
      this.logger.info("xx  [form-func]CloudDBZoneWrapper->insert Insert " + res + " records success");
    } catch (error) {
      this.logger.error("xx  [form-func]CloudDBZoneWrapper->insert executeInsert addressRecords failed " + error);
    }
  }
}

新增卡片函數(shù)form-insert,關(guān)鍵代碼如下:

import { CloudDBZoneWrapper } from '../clouddb/CloudDBZoneWrapper.js';
import * as Utils from '../utils/Utils.js';

export const myHandler = async function (event, context, callback, logger) {
  const credential = Utils.getCredential(context, logger);
  try {
    const cloudDBZoneWrapper = new CloudDBZoneWrapper(credential, logger);
    let formObj = cloudDBZoneWrapper.getForm(event);
    await cloudDBZoneWrapper.insert(formObj);

    callback({
      ret: { code: 0, desc: "SUCCESS" },
    });
  } catch (err) {
    logger.error("xx [form-func]insert func error:" + err.message + " stack:" + err.stack);
    callback({
      ret: { code: -1, desc: "ERROR" },
    });
  }
};

卡片云函數(shù)主入口,關(guān)鍵代碼如下:

let myHandler = async function (event, context, callback, logger) {
  let operation;
  let params;

  logger.info("xx enter form func with operation " + event.operation);
  operation = event.body ? JSON.parse(event.body).operation : event.operation;
  params = event.body ? JSON.parse(event.body).params : event.params;

  switch (operation) {
    case "query":
      query.myHandler(params, context, callback, logger);
      break;
    case "queryById":
      queryById.myHandler(params, context, callback, logger);
      break;
    case "insert":
      insert.myHandler(params, context, callback, logger);
      break;
    case "update":
      update.myHandler(params, context, callback, logger);
      break;
    case "delete":
      deleteByObj.myHandler(params, context, callback, logger);
      break;
    default:
      callback({
        ret: { code: -1, desc: "no such function" },
      });
  }

};
module.exports.myHandler = myHandler;

(2)成績云函數(shù)創(chuàng)建

展開CloudProgram -> cloudfunctions 右擊cloudfunctions目錄,創(chuàng)建 -> Cloud Function 輸入Cloud Function Name為score-func,點擊確認(rèn), 成績云函數(shù)里包含了增刪改查操作,所以在score-func下,創(chuàng)建不同的文件夾來區(qū)分,目錄結(jié)構(gòu)如下:

游戲萬能卡片- Serverless(端云一體化開發(fā))-開源基礎(chǔ)軟件社區(qū)

成績表云數(shù)據(jù)庫操作與卡片操作一樣,這里就不在重復(fù)了,可以參考一下上面卡片操作方法就可以。

5、云函數(shù)本地與遠程調(diào)試

(1)Run模式啟動調(diào)試

右擊“cloudfunctions”目錄,選擇“Run Cloud Functions”。

查看“Run”面板。如果出現(xiàn)“Cloud Functions loaded successfully”,表示所有函數(shù)已成功加載到本地運行的HTTP Server中,并生成對應(yīng)的POST URL。

在菜單欄選擇“Tools > CloudDev > Cloud Functions Requestor”,使用Cloud Functions Requestor觸發(fā)函數(shù)調(diào)用。

在彈出的“Cloud Functions Requestor”面板,填寫觸發(fā)事件參數(shù)。

點擊“Save”,可保存當(dāng)前觸發(fā)事件。

(2)Debug模式啟動調(diào)試

右擊“cloudfunctions”目錄,選擇“Run Cloud Functions”。

查看Console面板。如果出現(xiàn)“Cloud Functions loaded successfully”,表示函數(shù)成功加載到本地運行的HTTP Server中,并生成對應(yīng)的POST URL。

如需設(shè)置斷點調(diào)試,在函數(shù)代碼中選定要設(shè)置斷點的有效代碼行,在行號后單擊鼠標(biāo)左鍵設(shè)置斷點,設(shè)置斷點后,調(diào)試能夠在斷點處中斷,并高亮顯示該行。

在菜單欄選擇“Tools > CloudDev > Cloud Functions Requestor”,使用Cloud Functions Requestor觸發(fā)函數(shù)調(diào)用。

在彈出的“Cloud Functions Requestor”面板,填寫觸發(fā)事件參數(shù)。

點擊“Save”,可保存當(dāng)前觸發(fā)事件。

(3)自定義Run/Debug配置

在菜單欄選擇“Run > Edit Configurations”。

在“Run/Debug Configurations”窗口,點擊+,選擇“Cloud Functions”,新增一個Run/Debug配置。

自定義Run/Debug配置,完成后點擊“OK”。

· Name:Run/Debug配置的名稱,如“functions-custom1”。
· Server Port:HTTP服務(wù)端監(jiān)聽端口。默認(rèn)為“18090”,自定義端口號建議大于1024。勾選“Auto increment”表示如當(dāng)前端口被占用則端口號自動加“1”。
· Environment variables:函數(shù)運行的環(huán)境變量,為key-value形式。
點擊“Edit environment variables”按鈕,在“Environment Variables”彈窗中點擊“+”添加一個環(huán)境變量,然后點擊“OK”。添加成功后,您便可以將變量配置信息傳入到函數(shù)執(zhí)行環(huán)境中,用于函數(shù)運行時讀取。

選擇剛剛自定義的Run/Debug配置,分別點擊Run或Debug。后續(xù)調(diào)試步驟與默認(rèn)配置下的調(diào)試步驟一致,請分別參見Run模式啟動調(diào)試或Debug模式啟動調(diào)試。

(4)測試

實現(xiàn)云函數(shù)調(diào)用云數(shù)據(jù)庫,需要您先部署云工程,云端才會有相關(guān)數(shù)據(jù)及環(huán)境變量。同時,云函數(shù)為訪問云數(shù)據(jù)庫使用了“PROJECT_CREDENTIAL”環(huán)境變量,部署函數(shù)到AGC云端時,云端會自動配置好“PROJECT_CREDENTIAL”以運行環(huán)境變量。但在本地調(diào)試函數(shù)時,需要您手動將“PROJECT_CREDENTIAL”環(huán)境變量添加到Run/Debug配置中。否則,函數(shù)調(diào)試代碼執(zhí)行會因獲取不到“PROJECT_CREDENTIAL”環(huán)境變量而中斷。

游戲萬能卡片- Serverless(端云一體化開發(fā))-開源基礎(chǔ)軟件社區(qū)

從AGC獲取的“PROJECT_CREDENTIAL”環(huán)境變量添加到調(diào)試配置中。您也可以添加您需要的其他環(huán)境變量。

游戲萬能卡片- Serverless(端云一體化開發(fā))-開源基礎(chǔ)軟件社區(qū)

游戲萬能卡片- Serverless(端云一體化開發(fā))-開源基礎(chǔ)軟件社區(qū)

添加完環(huán)境變量后,啟動函數(shù),再點擊Trigger, 就可以看到成功返回數(shù)據(jù)了。

游戲萬能卡片- Serverless(端云一體化開發(fā))-開源基礎(chǔ)軟件社區(qū)

6、代碼講解

(1)云函數(shù)調(diào)用公共類

DatabaseUtils.ets云函數(shù)操作類部分代碼如下:

export class DatabaseUtils {

  async callWithParams(context, trigger, operation, params) {
    await getAGConnect(context);
    let body = {
      "operation": operation,
      "params": params
    }

    try {
      let functionCallable = agconnect.function().wrap(trigger);
      let functionResult = await functionCallable.call(body);
      return functionResult.getValue();
      // return functionResult.getValue().result;
    }
    catch (err) {
      return {
        "ret": {"code": -1, "desc": "ERROR"}
      }
    }
  }
    
  async invoke(context: any, trigger?: string, operation?: string, params?: object) {
    console.info(CommonConstants.DATABASE_TAG, 'xx invoke params: '+JSON.stringify(params))
    return await this.callWithParams(context, trigger, operation, params);
  }

  /**
   * 插入卡片數(shù)據(jù)。
   *
   * @param{Form}Form表單實體。
   * @param{DataRdb.RdbStore}RDB存儲RDB數(shù)據(jù)庫。
   * @return返回操作信息。
   */
  async insertForm(context: any, form: Form) {
    let res = await this.invoke(context, Triggers.FormFunc, RequestType.Insert, form);
    console.info(CommonConstants.DATABASE_TAG, 'xx insertForm result: ' + JSON.stringify(res));
  }
  ......
}

(2)卡片Ability調(diào)用公共類

EntryFormAbility.ets卡片生命周期代碼如下:

onAddForm(want) {
    // 獲取卡片ID:ohos.extra.param.key.form_identity
    let formId: string = want.parameters[CommonConstants.FORM_PARAM_IDENTITY_KEY] as string;
    // 獲取卡片名稱:ohos.extra.param.key.form_name
    let formName: string = want.parameters[CommonConstants.FORM_PARAM_NAME_KEY] as string;
    // 獲取卡片規(guī)格:ohos.extra.param.key.form_dimension
    let dimensionFlag: number = want.parameters[CommonConstants.FORM_PARAM_DIMENSION_KEY] as number;

    // 卡片信息
    let form: Form = new Form();
    form.formId = formId;
    form.formName = formName;
    form.dimension = dimensionFlag;

    // 保存卡片信息到數(shù)據(jù)庫
    DatabaseUtils.insertForm(this.context, form);
    // 獲取最優(yōu)成績
    getScoreById(this.context, dimensionFlag, formId);

    // 每五分鐘刷新一次
    formProvider.setFormNextRefreshTime(formId, CommonConstants.FORM_NEXT_REFRESH_TIME, (error, data) => {
      if (error) {
        console.error(CommonConstants.ENTRY_FORM_ABILITY_TAG, 'xx onAddForm 更新卡片失?。? + JSON.stringify(error))
      } else {
        console.info(CommonConstants.ENTRY_FORM_ABILITY_TAG, 'xx onAddForm 更新卡片成功')
      }
    });

    // 返回初始化卡片數(shù)據(jù)
    let formData: FormData = new FormData();
    formData.formId = formId;
    formData.bestScore = 0;
    formData.matrixNum = '1x1';
    formData.totalBestScore = 0;
    return formBindingData.createFormBindingData(formData);
  }

(3)主界面調(diào)用公共類

@Entry
@Component
struct Index {
  @State scoreDataList: Array<FormData> = []

  aboutToAppear() {
    // 請求通知欄權(quán)限
    this.requestNotification();
    // 更新卡片信息
    DatabaseUtils.updateForms(getContext(this));
    // 獲取成績歷史記錄
    this.getScoreListData()
  }
  onPageShow() {
    // 更新卡片信息
    DatabaseUtils.updateForms(getContext(this));
    // 獲取成績歷史記錄
    this.getScoreListData()
  }
    // 獲取成績歷史數(shù)據(jù)
  getScoreListData() {
    DatabaseUtils.getScoreListData(getContext(this))
      .then((res) => {
        this.scoreDataList = res;
        // 發(fā)送通知
        NotificationUtils.sendNotifications(this.scoreDataList[0].totalBestScore);
      }).catch((error) => {
      console.error(CommonConstants.MAIN_PAGE_TAG, 'xx aboutToAppear or onPageShow getScoreListData error ' + JSON.stringify(error));
    });
  }

  build() {...}
}

7、總結(jié)

通過把之前小游戲元服務(wù)-關(guān)系型數(shù)據(jù)庫修改為使用Serverless云函數(shù)、云數(shù)據(jù)庫,學(xué)習(xí)到不少知識,開始時不懂得怎么使用云函數(shù)調(diào)用云數(shù)據(jù)庫,一邊參考官方商城模板,一邊測試,到使用到這個小游戲上, 總結(jié)這個項目用到以下知識點:

  • 使用notification發(fā)布通知。
  • 使用端云一體化開發(fā)、開發(fā)云函數(shù)、開發(fā)云數(shù)據(jù)庫。
  • 使用FormExtensionAbility創(chuàng)建、更新、刪除元服務(wù)卡片。

備注:資源文件是我在學(xué)習(xí)云函數(shù)調(diào)用云數(shù)據(jù)庫寫的一個簡單實例,有云函數(shù)調(diào)用云數(shù)據(jù)庫需求的小伙伴可以下載下來參考一下。

文章相關(guān)附件可以點擊下面的原文鏈接前往下載:

https://ost.51cto.com/resource/2803

想了解更多關(guān)于開源的內(nèi)容,請訪問:

51CTO 開源基礎(chǔ)軟件社區(qū)

https://ost.51cto.com

責(zé)任編輯:jianghua 來源: 51CTO 開源基礎(chǔ)軟件社區(qū)
相關(guān)推薦

2023-03-14 21:19:29

云函數(shù)云數(shù)據(jù)庫

2023-03-15 16:24:43

云數(shù)據(jù)庫代碼開發(fā)

2023-08-07 14:09:58

數(shù)據(jù)庫開發(fā)

2023-05-31 15:42:06

游戲開發(fā)關(guān)系型數(shù)據(jù)庫

2023-08-04 17:43:31

2023-08-09 15:01:21

2015-05-15 13:56:53

云端一體

2020-01-14 08:58:38

Serverless框架web

2023-03-22 09:00:38

2010-05-11 11:12:26

2019-07-26 15:25:23

青云QingCloud云計算

2012-05-07 17:09:52

2012-06-07 08:52:08

微軟云計算Windows

2014-05-12 15:51:03

浪潮BIM一體化

2016-11-24 12:07:42

Android萬能圓角ImageView

2009-09-07 23:09:17

2017-04-26 21:08:22

研發(fā)協(xié)同云
點贊
收藏

51CTO技術(shù)棧公眾號