55.HarmonyOS NEXT 登录模块开发教程(九):部署与发布

温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦!

HarmonyOS NEXT 登录模块开发教程(九):部署与发布

效果预览

1. 引言

在前八篇教程中,我们介绍了HarmonyOS NEXT登录模块的整体架构、模态窗口的实现原理、一键登录页面的实现、短信验证码登录的实现、状态管理和数据绑定机制、安全性考虑、UI设计和用户体验优化、性能优化和最佳实践以及测试与调试技巧。本篇教程将深入讲解登录模块的部署和发布流程,帮助开发者将登录功能顺利部署到实际环境中。

部署和发布是应用开发的最后一环,也是将开发成果转化为用户价值的关键步骤。在HarmonyOS NEXT中,应用的部署和发布有其特定的流程和要求,本教程将详细介绍这些内容,确保登录模块能够顺利地集成到应用中并发布到应用市场。

2. 应用打包与签名

2.1 应用打包概述

HarmonyOS NEXT应用的打包过程主要包括以下步骤:

  1. 编译代码:将ArkTS代码编译为可执行文件
  2. 打包资源:将图片、字符串等资源文件打包
  3. 生成HAP包:生成Harmony Ability Package(HAP)文件
  4. 签名验证:对HAP包进行签名,确保其完整性和来源可信

2.2 使用DevEco Studio打包应用

DevEco Studio提供了图形化界面,简化了应用打包过程:

  1. 在DevEco Studio中,选择菜单栏的Build > Build Hap(s)/APP(s) > Build APP(s)
  2. 在弹出的对话框中,选择要构建的模块和配置
  3. 点击OK按钮,开始构建过程
  4. 构建完成后,HAP包将生成在项目的build/outputs/app/debugbuild/outputs/app/release目录下

2.3 应用签名

应用签名是确保应用完整性和来源可信的重要机制。HarmonyOS NEXT要求所有应用必须经过签名才能安装和运行。

2.3.1 创建签名证书
# 使用keytool创建签名证书
keytool -genkeypair -alias key0 -keyalg RSA -keysize 2048 -validity 3650 -keystore my_application.p12 -storetype PKCS12 -storepass 123456
n```在交互式提示中,需要输入以下信息:
- 名字与姓氏(CN)
- 组织单位名称(OU)
- 组织名称(O)
- 城市或区域名称(L)
- 省/市/自治区名称(ST)
- 国家/地区代码(C)#### 2.3.2 配置签名信息在项目的`build-profile.json5`文件中配置签名信息:```json
{"app": {"signingConfigs": [{"name": "release","type": "HarmonyOS","material": {"certpath": "C:/Users/Username/my_application.cer","storePassword": "123456","keyAlias": "key0","keyPassword": "123456","profile": "C:/Users/Username/my_application.p7b","signAlg": "SHA256withECDSA"}}],"compileSdkVersion": 9,"compatibleSdkVersion": 9,"products": [{"name": "default","signingConfig": "release"}]},"modules": [// 模块配置]
}
2.3.3 使用DevEco Studio签名应用
  1. 在DevEco Studio中,选择菜单栏的Build > Build Hap(s)/APP(s) > Build APP(s)
  2. 在弹出的对话框中,选择Release模式和之前配置的签名信息
  3. 点击OK按钮,开始构建和签名过程

3. 环境配置与切换

3.1 多环境配置

在实际开发中,通常需要为不同的环境(开发、测试、生产)配置不同的参数,如API地址、日志级别等。

3.1.1 使用配置文件

创建不同环境的配置文件:

// config/dev.ets - 开发环境配置
export default {API_BASE_URL: 'https://dev-api.example.com',LOG_LEVEL: 'DEBUG',ENABLE_MOCK: true
};// config/test.ets - 测试环境配置
export default {API_BASE_URL: 'https://test-api.example.com',LOG_LEVEL: 'INFO',ENABLE_MOCK: false
};// config/prod.ets - 生产环境配置
export default {API_BASE_URL: 'https://api.example.com',LOG_LEVEL: 'ERROR',ENABLE_MOCK: false
};
3.1.2 环境切换机制

创建一个环境管理模块,用于加载和切换环境配置:

// utils/env.ets
import devConfig from '../config/dev';
import testConfig from '../config/test';
import prodConfig from '../config/prod';// 环境类型
export enum EnvType {DEV = 'dev',TEST = 'test',PROD = 'prod'
}// 当前环境
let currentEnv: EnvType = EnvType.DEV;// 环境配置映射
const configMap = {[EnvType.DEV]: devConfig,[EnvType.TEST]: testConfig,[EnvType.PROD]: prodConfig
};// 获取当前环境配置
export function getConfig() {return configMap[currentEnv];
}// 设置当前环境
export function setEnv(env: EnvType) {currentEnv = env;
}// 获取当前环境类型
export function getEnv(): EnvType {return currentEnv;
}
3.1.3 在登录模块中使用环境配置
// services/auth.ets
import { getConfig } from '../utils/env';
import http from '@ohos.net.http';// 发送验证码
export async function sendVerifyCode(phoneNumber: string): Promise<boolean> {const config = getConfig();const url = `${config.API_BASE_URL}/auth/sendVerifyCode`;// 如果启用了模拟模式,直接返回成功if (config.ENABLE_MOCK) {return true;}try {const httpRequest = http.createHttp();const response = await httpRequest.request(url, {method: http.RequestMethod.POST,extraData: { phoneNumber },connectTimeout: 60000,readTimeout: 60000});httpRequest.destroy();if (response.responseCode === 200) {const result = JSON.parse(response.result.toString());return result.success;}return false;} catch (error) {console.error(`Failed to send verify code: ${error}`);return false;}
}

3.2 构建变体

使用DevEco Studio的构建变体功能,可以更方便地管理不同环境的构建配置:

  1. 在项目的build-profile.json5文件中配置构建变体:
{"app": {// 应用配置},"modules": [{"name": "entry","srcPath": "./entry","targets": [{"name": "default","applyToProducts": ["default"]}],"buildVariants": [{"name": "dev","productFlavors": [{"name": "flavor1","value": "dev"}]},{"name": "test","productFlavors": [{"name": "flavor1","value": "test"}]},{"name": "prod","productFlavors": [{"name": "flavor1","value": "prod"}]}]}]
}
  1. 在代码中获取当前构建变体:
import bundleManager from '@ohos.bundle.bundleManager';// 获取当前构建变体
async function getBuildVariant(): Promise<string> {try {const bundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_METADATA);const metadata = bundleInfo.metadata;return metadata['flavor1'] || 'dev'; // 默认为dev} catch (error) {console.error(`Failed to get build variant: ${error}`);return 'dev'; // 默认为dev}
}// 根据构建变体设置环境
async function initEnv() {const variant = await getBuildVariant();switch (variant) {case 'dev':setEnv(EnvType.DEV);break;case 'test':setEnv(EnvType.TEST);break;case 'prod':setEnv(EnvType.PROD);break;default:setEnv(EnvType.DEV);}
}

4. 版本管理

4.1 版本号规范

HarmonyOS NEXT应用的版本号由两部分组成:

  1. 版本名称(versionName):用于向用户展示的版本号,通常采用语义化版本号格式(主版本号.次版本号.修订号)
  2. 版本号(versionCode):用于系统识别的整数版本号,每次更新都应该递增

在项目的module.json5文件中配置版本信息:

{"module": {"name": "entry","type": "entry","description": "$string:module_desc","mainElement": "EntryAbility","deviceTypes": ["phone","tablet"],"deliveryWithInstall": true,"installationFree": false,"pages": "$profile:main_pages","abilities": [{"name": "EntryAbility","srcEntry": "./ets/entryability/EntryAbility.ets","description": "$string:EntryAbility_desc","icon": "$media:icon","label": "$string:EntryAbility_label","startWindowIcon": "$media:icon","startWindowBackground": "$color:start_window_background","skills": [{"entities": ["entity.system.home"],"actions": ["action.system.home"]}]}],"requestPermissions": [{"name": "ohos.permission.INTERNET"}]},"app": {"bundleName": "com.example.myapplication","vendor": "example","versionCode": 1000000,"versionName": "1.0.0"}
}

4.2 版本更新策略

在更新应用时,应遵循以下版本更新策略:

  1. 主版本号:当进行不兼容的API更改时递增
  2. 次版本号:当添加向下兼容的功能时递增
  3. 修订号:当进行向下兼容的问题修复时递增
  4. 版本号(versionCode):每次发布都递增,建议使用格式:主版本号×1000000 + 次版本号×1000 + 修订号

4.3 版本更新检测

在应用中实现版本更新检测功能,提醒用户更新到最新版本:

// services/update.ets
import http from '@ohos.net.http';
import promptAction from '@ohos.promptAction';
import bundleManager from '@ohos.bundle.bundleManager';// 检查更新
export async function checkUpdate(): Promise<void> {try {// 获取当前应用版本信息const bundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT);const currentVersionCode = bundleInfo.versionCode;const currentVersionName = bundleInfo.versionName;// 从服务器获取最新版本信息const httpRequest = http.createHttp();const response = await httpRequest.request('https://api.example.com/app/version', {method: http.RequestMethod.GET,connectTimeout: 60000,readTimeout: 60000});httpRequest.destroy();if (response.responseCode === 200) {const result = JSON.parse(response.result.toString());const latestVersionCode = result.versionCode;const latestVersionName = result.versionName;const updateUrl = result.updateUrl;const updateDescription = result.description;// 检查是否需要更新if (latestVersionCode > currentVersionCode) {// 显示更新提示promptAction.showDialog({title: '发现新版本',message: `当前版本:${currentVersionName}\n最新版本:${latestVersionName}\n\n${updateDescription}`,buttons: [{text: '稍后再说',color: '#666666'},{text: '立即更新',color: '#0000ff'}],success: (result) => {if (result.index === 1) {// 用户点击了立即更新,跳转到更新页面或应用市场// 实际实现可能需要调用系统API打开浏览器或应用市场console.info(`Update URL: ${updateUrl}`);}}});} else {console.info('当前已是最新版本');}}} catch (error) {console.error(`Failed to check update: ${error}`);}
}

5. 应用市场发布

5.1 华为应用市场发布流程

将应用发布到华为应用市场(AppGallery)的主要步骤:

  1. 注册开发者账号:在华为开发者联盟注册开发者账号
  2. 创建应用:在华为开发者联盟控制台创建应用
  3. 上传应用包:上传签名后的HAP包
  4. 填写应用信息:填写应用名称、描述、分类、标签、截图等信息
  5. 设置定价和发布范围:设置应用的定价策略和发布国家/地区
  6. 提交审核:提交应用进行审核
  7. 发布应用:审核通过后发布应用

5.2 应用发布前的检查清单

在发布应用前,应进行以下检查:

  1. 功能完整性:确保所有功能正常工作
  2. 兼容性:在不同设备和系统版本上测试
  3. 性能:检查应用的启动时间、响应速度、内存占用等
  4. 安全性:确保敏感数据加密、网络通信安全等
  5. 隐私合规:检查隐私政策是否完整,权限申请是否合理
  6. 资源完整:确保所有图片、文本等资源文件完整
  7. 版本信息:检查版本号是否正确
  8. 签名验证:确保应用已正确签名

5.3 灰度发布策略

灰度发布是一种将新版本逐步推广给用户的策略,可以降低风险:

  1. 阶段性发布:先发布给小部分用户,逐步扩大发布范围
  2. 用户分组:根据用户特征(如地区、设备类型)进行分组发布
  3. 监控反馈:密切监控用户反馈和应用性能
  4. 快速响应:发现问题时能够快速响应和修复

在华为应用市场中,可以通过以下方式实现灰度发布:

  1. 在发布新版本时,选择"分阶段发布"选项
  2. 设置每个阶段的用户比例和时间间隔
  3. 根据每个阶段的反馈情况,决定是否继续推进或回滚

6. 登录模块的部署注意事项

6.1 服务端接口对接

登录模块需要与服务端接口对接,确保通信正常:

  1. 接口文档:明确接口的URL、参数、返回值等
  2. 错误处理:处理各种可能的错误情况
  3. 超时设置:设置合理的超时时间
  4. 重试机制:实现请求失败后的重试机制
// services/auth.ets
import http from '@ohos.net.http';
import { getConfig } from '../utils/env';// 登录接口
export async function login(phoneNumber: string, verifyCode: string): Promise<any> {const config = getConfig();const url = `${config.API_BASE_URL}/auth/login`;try {const httpRequest = http.createHttp();const response = await httpRequest.request(url, {method: http.RequestMethod.POST,extraData: {phoneNumber,verifyCode},connectTimeout: 30000,readTimeout: 30000});httpRequest.destroy();if (response.responseCode === 200) {return JSON.parse(response.result.toString());}throw new Error(`Login failed with code: ${response.responseCode}`);} catch (error) {console.error(`Login error: ${error}`);// 实现重试逻辑return await retryLogin(phoneNumber, verifyCode);}
}// 重试登录
async function retryLogin(phoneNumber: string, verifyCode: string, maxRetries = 3): Promise<any> {let retries = 0;let lastError;while (retries < maxRetries) {try {const config = getConfig();const url = `${config.API_BASE_URL}/auth/login`;const httpRequest = http.createHttp();const response = await httpRequest.request(url, {method: http.RequestMethod.POST,extraData: {phoneNumber,verifyCode},connectTimeout: 30000,readTimeout: 30000});httpRequest.destroy();if (response.responseCode === 200) {return JSON.parse(response.result.toString());}throw new Error(`Login failed with code: ${response.responseCode}`);} catch (error) {lastError = error;retries++;// 等待一段时间后重试await new Promise(resolve => setTimeout(resolve, 1000 * retries));}}throw lastError;
}

6.2 用户数据迁移

在更新应用时,可能需要迁移用户数据:

  1. 数据备份:在更新前备份用户数据
  2. 数据迁移:实现数据格式转换和迁移逻辑
  3. 兼容处理:处理新旧版本数据格式不兼容的情况
// utils/dataMigration.ets
import data_preferences from '@ohos.data.preferences';// 数据迁移
export async function migrateUserData(): Promise<void> {try {const context = getContext(this);const oldPreferences = await data_preferences.getPreferences(context, 'user_data_v1');const newPreferences = await data_preferences.getPreferences(context, 'user_data_v2');// 检查是否需要迁移const migrated = await newPreferences.get('data_migrated', false);if (migrated) {return; // 已经迁移过,无需再次迁移}// 获取旧数据const userId = await oldPreferences.get('userId', '');const token = await oldPreferences.get('token', '');const loginTime = await oldPreferences.get('loginTime', 0);// 迁移到新格式if (userId) {await newPreferences.put('user_id', userId); // 新的键名格式await newPreferences.put('auth_token', token); // 新的键名格式await newPreferences.put('last_login_timestamp', loginTime); // 新的键名格式// 标记为已迁移await newPreferences.put('data_migrated', true);await newPreferences.flush();console.info('User data migration completed');}} catch (error) {console.error(`Data migration failed: ${error}`);}
}

6.3 多端同步

对于支持多端登录的应用,需要考虑数据同步问题:

  1. 用户标识:使用统一的用户标识符
  2. 数据同步:实现多端数据同步机制
  3. 冲突解决:处理数据冲突情况

7. 最佳实践与注意事项

在部署和发布登录模块时,有以下几点最佳实践和注意事项:

  1. 环境隔离:严格隔离开发、测试和生产环境
  2. 配置外部化:将配置参数外部化,便于不同环境切换
  3. 版本控制:使用语义化版本号,便于版本管理
  4. 发布节奏:建立规律的发布节奏,避免频繁更新
  5. 监控告警:部署监控和告警系统,及时发现问题
  6. 灰度策略:采用灰度发布策略,降低风险
  7. 回滚机制:建立快速回滚机制,应对紧急情况
  8. 用户反馈:收集和分析用户反馈,持续改进

8. 小结

本文详细介绍了HarmonyOS NEXT登录模块的部署和发布流程,包括应用打包与签名、环境配置与切换、版本管理、应用市场发布以及登录模块的部署注意事项。通过合理的部署和发布策略,可以确保登录模块稳定可靠地运行在用户设备上。

部署和发布是应用开发的最后一环,但同样重要。良好的部署和发布实践不仅能够提高应用的质量和稳定性,还能提升用户体验和满意度。在登录模块的部署和发布过程中,应注重安全性、稳定性和用户体验,确保用户能够顺利登录并使用应用。

9. 参考资源

  • HarmonyOS开发者文档 - 应用打包与签名
  • HarmonyOS开发者文档 - 应用发布
  • HarmonyOS开发者文档 - 版本管理
  • 华为应用市场开发者政策

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/72114.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3实现跨页面缓存

避免频繁向后端发送请求,vue3中,可以用缓存机制,为了实现跨页面缓存,可以把缓存放到localsotrage里面 关键代码: const globalCache JSON.parse(localStorage.getItem(globalCache)) || {}; 然后加一个forceRefresh关键字, const fetchData async (forceRefresh false) …

c++类和对象(下篇)上

今天又重新回到c的学习中~在前两篇博客中,我简单的学习了类的定义,实例化,以及类中的默认成员函数.下篇是类和对象的收尾篇,在这篇中我将补充一下中篇所讲的构造函数以及介绍一些类和对象的新知识.下面让我们开始学习吧. 再谈构造函数 在之前我们实现构造函数时,初始化成员变量…

深度学习 bert流程

Token IDs 在自然语言处理任务中&#xff0c;特别是使用预训练模型如BERT时&#xff0c;文本首先通过一个分词器&#xff08;例如 BertTokenizer&#xff09;转换为一系列的token IDs。这些ID是每个词或子词单元在词汇表&#xff08;包含汉字、英文单词、标点符号&#xff09;…

PPT内视频播放无法播放的原因及解决办法

PPT内视频无法播放&#xff0c;通常是视频编解码的问题。目前我遇到的常见的视频编码格式有H.264&#xff0c;H.265&#xff0c;VP9&#xff0c;AV1这4种。H.264编解码的视频&#xff0c;Windows原生系统可以直接播放&#xff0c;其他的视频编码格式需要安装对应的视频编解码插…

星越L_行李舱空间拓展讲解

目录 1.储物槽 2.底板盖储物空间 3.挂钩 3.左侧照明灯 4.第二排座椅放倒 1.储物槽 使用钥匙或者后备箱按钮打开电动后备箱,左侧储物槽可储藏物品。 2.底板盖储物空间 打开地板盖,下方有储物空间。并放置了随车工具。 3.挂钩 后备箱左右两测各有一个挂钩。

深度学习与大模型-矩阵

矩阵其实在我们的生活中也有很多应用&#xff0c;只是我们没注意罢了。 1. 矩阵是什么&#xff1f; 简单来说&#xff0c;矩阵就是一个长方形的数字表格。比如你有一个2行3列的矩阵&#xff0c;可以写成这样&#xff1a; 这个矩阵有2行3列&#xff0c;每个数字都有一个位置&a…

LuaJIT 学习(2)—— 使用 FFI 库的几个例子

文章目录 介绍Motivating Example: Calling External C Functions例子&#xff1a;Lua 中调用 C 函数 Motivating Example: Using C Data StructuresAccessing Standard System FunctionsAccessing the zlib Compression LibraryDefining Metamethods for a C Type例子&#xf…

基于 FastText、dlib 和 CppJieba 的中文语义相似度计算实践

在自然语言处理(NLP)领域,语义相似度计算是许多任务的核心,例如问答系统、文本检索和推荐系统。然而,中文因缺乏显式分词和复杂的语义结构,实现高效的语义对比具有一定挑战性。 本文将介绍如何结合 CppJieba(高效中文分词工具)、FastText(词向量模型)和 dlib(机器学…

HCIA-11.以太网链路聚合与交换机堆叠、集群

链路聚合背景 拓扑组网时为了高可用&#xff0c;需要网络的冗余备份。但增加冗余容易后会出现环路&#xff0c;所以我们部署了STP协议来破除环路。 但是&#xff0c;根据实际业务的需要&#xff0c;为网络不停的增加冗余是现实需要的一部分。 那么&#xff0c;为了让网络冗余…

Unity基于C#+UGUI解决方案,制作每日签到系统(本地存储签到数据)

一、需求介绍:基于本地存储系统制作一个每日签到系统界面,相关签到界面如下图所示,点击“签到有礼”按钮后就会跳转到“每日登录礼”这个界面,点击“立即签到”按钮之后,按钮就会置灰,而且按钮的文字会变成“等待明日”。 二、制作界面显示相关功能,需要在Unity中新建一…

AI本地部署

文档加载&#xff08;Document Loading&#xff09;&#xff1a;从多种不同来源加载文档。LangChain提供了100多种不同的文档加载器&#xff0c;包括PDF在内的非结构化的数据、SQL在内的结构化的数据&#xff0c;以及Python、Java之类的代码等​ •文本分割&#xff08;Splitti…

精准车型识别:视觉分析技术的力量

随着智慧城市和智能交通系统的快速发展&#xff0c;车型识别检测成为交通管理、安全监控和数据分析的关键技术之一。利用视觉分析的方式&#xff0c;我们可以高效、准确地检测监控下的车辆类型、车牌信息及车流量&#xff0c;为城市交通管理提供有力支持。本文将从背景、技术实…

上下文微调(Contextual Fine-Tuning, CFT)提高大型语言模型(LLMs)在特定领域的学习和推理能力

大型语言模型(LLMs)在开放领域任务中表现出色,但在快速演变的专业领域(如医学、金融)中面临挑战: 知识更新难题:传统指令微调(Instruction Fine-Tuning, IFT)依赖显式指令,难以适应动态知识。灾难性遗忘:持续预训练(Continued Pretraining, CPT)可能导致模型遗忘已…

在 LaTeX 中强制表格位于页面顶部

在 LaTeX 中强制表格位于页面顶部&#xff0c;可以通过以下 多种方法结合使用&#xff0c;按优先级推荐&#xff1a; 方法 1&#xff1a;使用 [!t] 位置限定符 原理&#xff1a;通过 [!t] 强制 LaTeX 优先将表格放置在页面顶部&#xff08;Top&#xff09;&#xff0c;! 表示忽…

kotlin与MVVM的结合使用总结(二)

在 MVVM&#xff08;Model - View - ViewModel&#xff09;架构中&#xff0c;M 层即 Model 层&#xff0c;主要负责数据的管理、存储和获取&#xff0c;它与业务逻辑和数据处理相关。在 Kotlin 中实现 MVVM 的 M 层&#xff0c;通常会涉及数据类的定义、数据的本地存储与远程获…

电子元器件选型与实战应用—16 怎么选一个合适的MCU芯片?

文章目录 1. 选型要素1.1 价格1.2 技术支持1.3 厂家优势1.4 功耗1.5 特殊功能1.6 统计外设1.7 确定外设占用的内存和flash大小1.8 确定外设通信接口1.9 确定外设通信接口的电平1.10 确定外设的GPIO数量1.11 确定外设的供电和功耗1.12 确定外设GPIO的种类1.13 确定ADC的数量1.14…

VSCode 搭建C++编程环境 2025新版图文安装教程(100%搭建成功,VSCode安装+C++环境搭建+运行测试+背景图设置)

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、VScode下载及安装二、安装 MinGW-w64 工具链三、Windows环境变量配置四、检查 M…

Django系列教程(7)——路由配置URLConf

目录 URLconf是如何工作的? path和re_path方法 更多URL配置示例 URL的命名及reverse()方法 使用命名URL 硬编码URL - 不建议 URL指向基于类的视图(View) 通过URL传递额外的参数 小结 Django的项目文件夹和每个应用(app)目录下都有urls.py文件&#xff0c;它们构成了D…

transformer bert 多头自注意力

输入的&#xff08;a1,a2,a3,a4&#xff09;是最终嵌入&#xff0c;是一个(512,768)的矩阵&#xff1b;而a1是一个token&#xff0c;尺寸是768 a1通过wq权重矩阵&#xff0c;经过全连接变换得到查询向量q1&#xff1b;a2通过Wk权重矩阵得到键向量k2&#xff1b;q和k点乘就是值…

Spring Boot + MyBatis-Plus 项目目录结构

以下是一个标准的 Spring Boot MyBatis-Plus 项目目录结构及文件命名规范&#xff0c;包含每个目录和文件的作用说明&#xff0c;适用于中大型项目开发&#xff1a; 项目根目录结构 src/ ├── main/ │ ├── java/ # Java 源代码 │ │ └── com/…