• [热门活动] 关于“云学堂集证有礼·码力全开”活动的提问
    我在通过4门微认证后立刻填写了领取优惠券的问卷,并没有等待到第二天,所以没有领取成功,但是我想重新填写问卷的时候,他自动重定向到其他地址去了。请问我还能申领优惠券吗
  • [案例共创] 【案例共创】远程开发环境中部署Bun + Next.js + tRPC与开发者空间生态版GaussDB实践操作并编写学生成绩管理系统
    余年方十七,习编程已有数载,虽非初学,然深知学无止境,技艺之精进,如逆水行舟,不进则退。 技艺渐精 昔日初学时,常为类型错误而苦恼,今已能熟练运用多种语言。ts之强大,Python之优雅,前端三剑客之灵动,后端框架之稳健,皆有所涉猎。数据结构与算法,亦略有心得,排序查找,动态规划,虽不敢言精通,然基本功底尚算扎实。 项目实战 近年来,陆续完成数个项目:网站开发、移动应用、数据分析工具等。每一次从需求分析到架构设计,从编码实现到测试部署,皆亲力亲为。虽过程艰辛,然收获颇丰。始知纸上谈兵易,实际开发难。用户体验、性能优化、代码维护,诸般考量,方显编程之深奥。 开源贡献 偶有小作,贡献于开源社区,虽微不足道,然能为他人所用,心甚慰藉。GitHub上星标虽少,然每一个fork,每一个issue,皆是对余之鞭策与鼓励。 技术视野 关注前沿技术,人工智能、区块链、云计算等,皆有所了解。深知技术日新月异,昨日之新技术,今日或已过时。唯有保持学习之心,方能不被时代所抛弃。 反思与展望 回首来路,虽有所成,然亦深感不足。代码质量有待提升,系统设计能力尚需磨练,团队协作经验亦显匮乏。他日若能进入知名企业,与高手切磋,定能更上一层楼。 夫编程者,匠心独运之艺也。既要有扎实之基础,又需具创新之思维。余虽年少,然志在千里,愿以代码改变世界,以技术服务社会。作者写此文时正值十七岁生日前夕,虽习编程数年,然经验尚浅,见解有限,往有不足之处,请在评论区指出。案例简介:本案例选择bun + Next.js作为示例编写了学生成绩管理系统,并借助开发者空间云开发环境提供的免费 GaussDB数据库和HCE2.0开发环境进行本地部署Next.js生态组件、轻松部署上云,直观地展示在实际应用开发中为开发者带来的便利。一、概述1.1 案例介绍Bun + Next.js + tRPC 是一个现代化的全栈 TypeScript Web 开发技术栈,可以快速开发高性能和可维护的网站。Bun 作为超快的 JavaScript 运行时和包管理器,Next.js 负责处理前端和后端开发中的复杂部分,tRPC 提供端到端类型安全的 API 层。在数据库操作方面,本技术栈支持直接使用原生 SQL 语句进行数据库交互,无需额外的 ORM 抽象层,既保证了查询性能的最优化,又提供了最大的灵活性。让开发者可以专注于编写业务逻辑,而无需重复造轮子。本案例借助开发者空间云开发环境部署 Next.js 项目,通过 tRPC 构建类型安全的 API 接口,并直接使用 SQL 语句与开发者空间提供的免费 GaussDB 数据库实例对接,完成一个现代化 Web 应用构建。通过实际操作,让大家深入了解如何利用 Bun + Next.js + tRPC + 原生 SQL 技术栈开发并部署一个高性能的全栈 Web 应用。华为开发者空间,是为全球开发者打造的专属开发者空间,致力于为每位开发者提供一台 云开发环境、一套开发工具和云上存储空间,汇聚昇腾、鸿蒙、鲲鹏、GaussDB、欧拉 等华为各项根技术的开发工具资源,并提供配套案例指导开发者 从开发编码到应用调 测,基于华为根技术生态高效便捷的知识学习、技术体验、应用创新。1.2 适用对象企业个人开发者初高中学生和高校学生1.3 案例时间本案例总时长预计120分钟。1.4 案例流程本案例将通过三个主要步骤,带领大家在华为云开发者空间上构建一个完整的 Bun + Next.js + tRPC + 原生 SQL 全栈应用:第一步:申请华为云开发者空间的 GaussDB 数据库注册并登录华为云开发者空间申请免费的 GaussDB 数据库实例获取数据库连接配置信息第二步:进行华为云开发者空间的云开发环境进行 Bun + Next.js + tRPC 安装与配置创建云开发环境实例安装 Bun 运行时环境初始化 Next.js TypeScript 项目集成 tRPC 框架,配置端到端类型安全的 API设置数据库连接和环境变量第三步:启用 Next.js Web 服务,适配 GaussDB,在客户端测试原生 SQL 的建表和数据操作编写原生 SQL 建表脚本通过 tRPC 封装数据库操作接口启动 Next.js 开发服务器在客户端测试数据库的增删改查功能验证端到端类型安全和性能表现通过这三个步骤,您将完整体验从环境搭建到应用部署的全流程,深入理解 Bun + Next.js + tRPC + 原生 SQL 技术栈在华为云开发者空间上的强大能力。1.5 资源总览本案例预计花费0.8元。资源名称规格单价(元)时长(分钟)华为开发者空间 - 云开发环境鲲鹏通用计算增强型 kc1 2vCPUs 4G免费40华为开发者空间 - 生态版GaussDB单副本集中式版 4 vCPUs 16G HCE OS 64bit (200GB)免费40弹性公网IP按流量计费 5Mbit/s0.8元/GB40二、案例准备工作2.1 配置云开发环境根据案例《开发者空间 - 云开发环境使用指导》,请查看下面链接,配置云开发环境,并通过xshell等链接工具登录云开发环境。开发者空间 - 云开发环境使用指导2.2 领取GaussDB数据库免费领取GaussDB在线试用版(2025年 06月 21日 - 2025年 12月 31日)。华为开发者空间-GaussDB云数据库领取与使用指导注:部署的Django项目需要对接GaussDB,因此GaussDB需要绑定EIP,参考上述指导中领取部分第(5)步bunx create-next-app@latest2.3 验证云开发环境与GaussDB互通通过xshell或windows命令终端登录云开发环境,使用ping [ip]测试GaussDB能否连接,如下是通的2.4 新建数据库及用户修改dn:password_encryption_type字段为1。0表示采用md5方式对密码加密。1表示采用sha256和md5两种方式分别对密码加密,2表示采用sha256方式对密码加密,3表示采用sm3方式对密码加密。MD5加密算法安全性低,存在安全风险,不建议使用。点击右上角登录数据库管理界面创建数据库使用如下图配置。通过右上角登录GaussDB的SQL操作界面,用如下SQL创建Django链接的登录用户。create user myuser with sysadmin password 'GaussDB@123'; 三、Bun + Next.js + Prisma 安装配置指南3.1 安装 Bun# 全局安装 bun sudo npm install -g bun # 验证安装 bun --version # 检查安装位置 which bun3.2 创建 Next.js 项目# 交互式创建next.js项目 bunx create-next-app@latest看到如下输出即为成功创建Next.js项目3.3测试Next.js是否安装成功# 查看项目结构 ls -la # 启动开发服务器测试 bun dev # 在另一个终端测试(如果需要) curl http://localhost:3000 3.4 安装trpc步骤 1: 安装依赖bun add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query zod superjson依赖说明@trpc/server - 服务器端 tRPC 核心@trpc/client - 客户端 tRPC 核心@trpc/react-query - React Query 集成@trpc/next - Next.js 集成@tanstack/react-query - 数据获取和缓存zod - 类型验证superjson - 序列化(支持 Date、BigInt 等)步骤 2: 创建服务器端配置创建 app/lib/trpc.tsimport { initTRPC } from '@trpc/server'; import superjson from 'superjson'; import { ZodError } from 'zod'; const t = initTRPC.create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.issues : null, }, }; }, }); export const createTRPCRouter = t.router; export const publicProcedure = t.procedure; 配置说明transformer: superjson - 使用 SuperJSON 进行序列化errorFormatter - 自定义错误格式化,支持 Zod 验证错误createTRPCRouter - 创建路由的工厂函数publicProcedure - 公开的 API 过程(无需认证)步骤 3: 创建路由tRPC 路由(Router)是服务端定义 API 端点和业务逻辑的核心组件,它通过类型安全的方式组织和管理所有的远程过程调用,包括查询(query)、变更(mutation)和订阅(subscription),并自动生成 TypeScript 类型定义供客户端使用,实现了从后端到前端的完整类型推断和 API 结构管理。创建主路由 app/lib/routers/_app.tsimport { createTRPCRouter } from '../trpc'; export const appRouter = createTRPCRouter({ }); export type AppRouter = typeof appRouter; 步骤 4: 创建 API 路由处理创建 app/api/trpc/[trpc]/route.tsimport { fetchRequestHandler } from '@trpc/server/adapters/fetch'; import { appRouter } from '../../../lib/routers/_app'; const handler = (req: Request) => fetchRequestHandler({ endpoint: '/api/trpc', req, router: appRouter, createContext: () => ({}), }); export { handler as GET, handler as POST }; 说明使用 Next.js App Router 的 API 路由fetchRequestHandler 处理 HTTP 请求createContext 创建请求上下文(当前为空对象)步骤 5: 创建客户端配置创建 utils/trpc.tsimport { createTRPCReact } from '@trpc/react-query'; import { type AppRouter } from '../app/lib/routers/_app'; export const trpc = createTRPCReact<AppRouter>(); 创建独立客户端 utils/trpc-client.tsimport { createTRPCProxyClient, httpBatchLink } from '@trpc/client'; import superjson from 'superjson'; import { type AppRouter } from '../app/lib/routers/_app'; export const trpcClient = createTRPCProxyClient<AppRouter>({ links: [ httpBatchLink({ url: '/api/trpc', transformer: superjson, }), ], }); 步骤 6: 设置提供者创建 app/providers.tsx'use client'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { httpBatchLink } from '@trpc/client'; import React, { useState } from 'react'; import superjson from 'superjson'; import { trpc } from '../utils/trpc'; export function TRPCProvider({ children }: { children: React.ReactNode }) { const [queryClient] = useState(() => new QueryClient()); const [trpcClient] = useState(() => trpc.createClient({ links: [ httpBatchLink({ url: '/api/trpc', transformer: superjson, }), ], }) ); return ( <trpc.Provider client={trpcClient} queryClient={queryClient}> <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> </trpc.Provider> ); } tRPC Provider 是一个配置组件,用于在客户端应用中设置 tRPC 客户端实例,它负责配置 API 端点、管理请求状态和缓存,并提供端到端的 TypeScript 类型安全,让前端能够直接调用后端函数而无需手动定义 API 接口类型。步骤 7: 集成到应用更新 app/layout.tsximport type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { TRPCProvider } from "./providers"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], }); export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang="en"> <body className={`${geistSans.variable} ${geistMono.variable} antialiased`} > <TRPCProvider>{children}</TRPCProvider> </body> </html> ); } 3.5 连接和初始化数据库并编写相关方法步骤1:安装sql相关依赖bun add pg dotenv bun add -d @types/bun @types/pg tsxdotenv 是一个零依赖的模块,它可以从 .env 文件中加载环境变量到 process.env 中。步骤2:设置数据库连接创建 .env# GaussDB 数据库配置 DB_HOST=your-gaussdb-host.com DB_PORT=3306 DB_USER=your-username DB_PASSWORD=your-password DB_NAME=your-database-name # 可选配置 DB_MAX_CONNECTIONS=20 说明DB_HOST就是你数据库IPDB_PORT为数据库端口默认是8000DB_USER和DB_PASSWORD就是你刚刚创建的用户的密码步骤3:编写数据库相关代码创建 scripts/init-database.ts 用来初始化数据库import { Client } from 'pg'; import dotenv from 'dotenv'; dotenv.config(); //这里连接到数据库,如果连接失败就去检查你的.env文件 const client = new Client({ host: process.env.DB_HOST || 'localhost', port: parseInt(process.env.DB_PORT || '5432'), database: process.env.DB_NAME || 'student_management', user: process.env.DB_USER || 'postgres', password: process.env.DB_PASSWORD || 'password', }); //这里开始初始化数据库 async function initDatabase() { try { console.log('连接数据库...'); await client.connect(); console.log('数据库连接成功'); // 创建学生表 console.log('创建学生表...'); await client.query(` CREATE TABLE IF NOT EXISTS students ( id SERIAL PRIMARY KEY, student_id VARCHAR(20) UNIQUE NOT NULL, name VARCHAR(100) NOT NULL, gender VARCHAR(10) CHECK (gender IN ('男', '女')), birth_date DATE, class_name VARCHAR(50), phone VARCHAR(20), email VARCHAR(100), address TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `); // 创建课程表 console.log('创建课程表...'); await client.query(` CREATE TABLE IF NOT EXISTS courses ( id SERIAL PRIMARY KEY, course_code VARCHAR(20) UNIQUE NOT NULL, course_name VARCHAR(100) NOT NULL, credits DECIMAL(3,1) NOT NULL, teacher_name VARCHAR(100), description TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `); // 创建成绩表 console.log('创建成绩表...'); await client.query(` CREATE TABLE IF NOT EXISTS grades ( id SERIAL PRIMARY KEY, student_id INTEGER REFERENCES students(id) ON DELETE CASCADE, course_id INTEGER REFERENCES courses(id) ON DELETE CASCADE, semester VARCHAR(20) NOT NULL, score DECIMAL(5,2) CHECK (score >= 0 AND score <= 100), grade VARCHAR(2) CHECK (grade IN ('A+', 'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'F')), exam_date DATE, remarks TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(student_id, course_id, semester) ); `); // 插入测试数据 console.log('插入测试数据...'); // 学生数据 实在想不出来什么好名字了。。。就张三李四吧,为啥都是高三呢因为作者高三了 await client.query(` INSERT INTO students (student_id, name, gender, birth_date, class_name, phone, email) VALUES ('2024001', '张三', '男', '2006-03-15', '高三(1)班', '13800138001', 'zhangsan@example.com'), ('2024002', '李四', '女', '2006-07-22', '高三(1)班', '13800138002', 'lisi@example.com'), ('2024003', '王五', '男', '2006-01-10', '高三(1)班', '13800138003', 'wangwu@example.com'), ON CONFLICT (student_id) DO NOTHING; `); // 课程数据 让高中生学高数是不是很好玩 await client.query(` INSERT INTO courses (course_code, course_name, credits, teacher_name, description) VALUES ('MATH101', '数学', 4.0, '陈老师', '高等数学基础课程'), ('CHI101', '语文', 4.0, '刘老师', '语文基础课程'), ('ENG101', '英语', 4.0, '王老师', '英语基础课程'), ('PHY101', '物理', 3.5, '李老师', '物理基础课程'), ('CHE101', '化学', 3.5, '张老师', '化学基础课程'), ('BIO101', '生物', 3.0, '赵老师', '生物基础课程') ON CONFLICT (course_code) DO NOTHING; `); // 成绩数据 await client.query(` INSERT INTO grades (student_id, course_id, semester, score, grade, exam_date) VALUES (1, 1, '2024-春季', 85.5, 'B+', '2024-06-15'), (1, 2, '2024-春季', 92.0, 'A', '2024-06-16'), (1, 3, '2024-春季', 78.5, 'C+', '2024-06-17'), (2, 1, '2024-春季', 95.0, 'A+', '2024-06-15'), (2, 2, '2024-春季', 88.5, 'B+', '2024-06-16'), (2, 3, '2024-春季', 91.0, 'A', '2024-06-17'), (3, 1, '2024-春季', 72.0, 'C', '2024-06-15'), (3, 2, '2024-春季', 85.0, 'B+', '2024-06-16'), (3, 3, '2024-春季', 79.5, 'C+', '2024-06-17') ON CONFLICT (student_id, course_id, semester) DO NOTHING; `); // 创建索引 console.log('创建索引...'); await client.query(` CREATE INDEX IF NOT EXISTS idx_students_student_id ON students(student_id); CREATE INDEX IF NOT EXISTS idx_students_class_name ON students(class_name); CREATE INDEX IF NOT EXISTS idx_courses_course_code ON courses(course_code); CREATE INDEX IF NOT EXISTS idx_grades_student_id ON grades(student_id); CREATE INDEX IF NOT EXISTS idx_grades_course_id ON grades(course_id); CREATE INDEX IF NOT EXISTS idx_grades_semester ON grades(semester); `); console.log('数据库初始化完成!'); // 显示统计 const stats = await client.query(` SELECT (SELECT COUNT(*) FROM students) as student_count, (SELECT COUNT(*) FROM courses) as course_count, (SELECT COUNT(*) FROM grades) as grade_count; `); console.log(`学生数量: ${stats.rows[0].student_count}`); console.log(`课程数量: ${stats.rows[0].course_count}`); console.log(`成绩记录: ${stats.rows[0].grade_count}`); } catch (error) { console.error('数据库初始化失败:', error); throw error; } finally { await client.end(); console.log('数据库连接已关闭'); } } if (require.main === module) { initDatabase() .then(() => { console.log('数据库初始化完成!'); process.exit(0); }) .catch((error) => { console.error('初始化失败:', error); process.exit(1); }); } export { initDatabase }; 创建 types/database.ts 类型文件// 学生信息 export interface Student { id: number; student_id: string; name: string; gender: '男' | '女'; birth_date: Date | null; class_name: string | null; phone: string | null; email: string | null; address: string | null; created_at: Date; updated_at: Date; } // 课程信息 export interface Course { id: number; course_code: string; course_name: string; credits: number; teacher_name: string | null; description: string | null; created_at: Date; updated_at: Date; } // 成绩信息 export interface Grade { id: number; student_id: number; course_id: number; semester: string; score: number | null; grade: 'A+' | 'A' | 'A-' | 'B+' | 'B' | 'B-' | 'C+' | 'C' | 'C-' | 'D+' | 'D' | 'F' | null; exam_date: Date | null; remarks: string | null; created_at: Date; updated_at: Date; } // 成绩详情(包含学生和课程信息) export interface GradeWithDetails { id: number; student_id: number; course_id: number; semester: string; score: number | null; grade: string | null; exam_date: Date | null; remarks: string | null; student_name: string; student_id_code: string; course_name: string; course_code: string; teacher_name: string | null; created_at: Date; updated_at: Date; } // 学生成绩统计 export interface StudentGradeStats { student_id: number; student_name: string; student_id_code: string; class_name: string | null; total_courses: number; average_score: number; total_credits: number; gpa: number; } // 课程成绩统计 export interface CourseGradeStats { course_id: number; course_name: string; course_code: string; teacher_name: string | null; total_students: number; average_score: number; highest_score: number; lowest_score: number; pass_rate: number; } // 查询参数 export interface StudentQueryParams { page?: number; limit?: number; search?: string; class_name?: string; gender?: '男' | '女'; } export interface GradeQueryParams { page?: number; limit?: number; student_id?: number; course_id?: number; semester?: string; min_score?: number; max_score?: number; } // 创建学生参数 export interface CreateStudentParams { student_id: string; name: string; gender: '男' | '女'; birth_date?: Date; class_name?: string; phone?: string; email?: string; address?: string; } // 更新学生参数 export interface UpdateStudentParams { name?: string; gender?: '男' | '女'; birth_date?: Date; class_name?: string; phone?: string; email?: string; address?: string; } // 创建成绩参数 export interface CreateGradeParams { student_id: number; course_id: number; semester: string; score?: number; grade?: string; exam_date?: Date; remarks?: string; } // 更新成绩参数 export interface UpdateGradeParams { score?: number; grade?: string; exam_date?: Date; remarks?: string; } // 创建课程参数 export interface CreateCourseParams { course_code: string; course_name: string; credits: number; teacher_name?: string; description?: string; } // 更新课程参数 export interface UpdateCourseParams { course_name?: string; credits?: number; teacher_name?: string; description?: string; } 创建lib/database.ts提供相关数据库操作方法import { Pool, PoolClient } from 'pg'; import dotenv from 'dotenv'; import { Student, Course, Grade, GradeWithDetails, StudentGradeStats, CourseGradeStats, StudentQueryParams, GradeQueryParams, CreateStudentParams, UpdateStudentParams, CreateGradeParams, UpdateGradeParams, CreateCourseParams, UpdateCourseParams, } from '../types/database'; dotenv.config(); class DatabaseService { private pool: Pool; constructor() { this.pool = new Pool({ host: process.env.DB_HOST || 'localhost', port: parseInt(process.env.DB_PORT || '5432'), database: process.env.DB_NAME || 'student_management', user: process.env.DB_USER || 'postgres', password: process.env.DB_PASSWORD || 'password', max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }); // 监听连接错误 this.pool.on('error', (err) => { console.error('数据库连接池错误:', err); }); } // 获取数据库连接 async getClient(): Promise<PoolClient> { return await this.pool.connect(); } // 关闭连接池 async close(): Promise<void> { await this.pool.end(); } // 学生相关方法 async getStudents(params: StudentQueryParams = {}): Promise<Student[]> { const { page = 1, limit = 10, search, class_name, gender } = params; const offset = (page - 1) * limit; let query = 'SELECT * FROM students WHERE 1=1'; const values: any[] = []; let paramIndex = 1; if (search) { query += ` AND (name ILIKE $${paramIndex} OR student_id ILIKE $${paramIndex})`; values.push(`%${search}%`); paramIndex++; } if (class_name) { query += ` AND class_name = $${paramIndex}`; values.push(class_name); paramIndex++; } if (gender) { query += ` AND gender = $${paramIndex}`; values.push(gender); paramIndex++; } query += ` ORDER BY created_at DESC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`; values.push(limit, offset); const client = await this.getClient(); try { const result = await client.query(query, values); return result.rows; } finally { client.release(); } } async getStudentById(id: number): Promise<Student | null> { const client = await this.getClient(); try { const result = await client.query('SELECT * FROM students WHERE id = $1', [id]); return result.rows[0] || null; } finally { client.release(); } } async getStudentByStudentId(studentId: string): Promise<Student | null> { const client = await this.getClient(); try { const result = await client.query('SELECT * FROM students WHERE student_id = $1', [studentId]); return result.rows[0] || null; } finally { client.release(); } } async createStudent(params: CreateStudentParams): Promise<Student> { const client = await this.getClient(); try { const result = await client.query(` INSERT INTO students (student_id, name, gender, birth_date, class_name, phone, email, address) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING * `, [ params.student_id, params.name, params.gender, params.birth_date, params.class_name, params.phone, params.email, params.address, ]); return result.rows[0]; } finally { client.release(); } } async updateStudent(id: number, params: UpdateStudentParams): Promise<Student | null> { const client = await this.getClient(); try { const fields = Object.keys(params).filter(key => params[key as keyof UpdateStudentParams] !== undefined); if (fields.length === 0) return null; const setClause = fields.map((field, index) => `${field} = $${index + 2}`).join(', '); const values = [id, ...fields.map(field => params[field as keyof UpdateStudentParams])]; const result = await client.query(` UPDATE students SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = $1 RETURNING * `, values); return result.rows[0] || null; } finally { client.release(); } } async deleteStudent(id: number): Promise<boolean> { const client = await this.getClient(); try { const result = await client.query('DELETE FROM students WHERE id = $1', [id]); return (result.rowCount || 0) > 0; } finally { client.release(); } } // 课程相关方法 async getCourses(): Promise<Course[]> { const client = await this.getClient(); try { const result = await client.query('SELECT * FROM courses ORDER BY course_code'); return result.rows; } finally { client.release(); } } async getCourseById(id: number): Promise<Course | null> { const client = await this.getClient(); try { const result = await client.query('SELECT * FROM courses WHERE id = $1', [id]); return result.rows[0] || null; } finally { client.release(); } } async createCourse(params: CreateCourseParams): Promise<Course> { const client = await this.getClient(); try { const result = await client.query(` INSERT INTO courses (course_code, course_name, credits, teacher_name, description) VALUES ($1, $2, $3, $4, $5) RETURNING * `, [ params.course_code, params.course_name, params.credits, params.teacher_name, params.description, ]); return result.rows[0]; } finally { client.release(); } } async updateCourse(id: number, params: UpdateCourseParams): Promise<Course | null> { const client = await this.getClient(); try { const fields = Object.keys(params).filter(key => params[key as keyof typeof params] !== undefined); if (fields.length === 0) return null; const setClause = fields.map((field, index) => `${field} = $${index + 2}`).join(', '); const values = [id, ...fields.map(field => params[field as keyof typeof params])]; const result = await client.query(` UPDATE courses SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = $1 RETURNING * `, values); return result.rows[0] || null; } finally { client.release(); } } // 成绩相关方法 async getGrades(params: GradeQueryParams = {}): Promise<GradeWithDetails[]> { const { page = 1, limit = 10, student_id, course_id, semester, min_score, max_score } = params; const offset = (page - 1) * limit; let query = ` SELECT g.*, s.name as student_name, s.student_id as student_id_code, c.course_name, c.course_code, c.teacher_name FROM grades g JOIN students s ON g.student_id = s.id JOIN courses c ON g.course_id = c.id WHERE 1=1 `; const values: any[] = []; let paramIndex = 1; if (student_id) { query += ` AND g.student_id = $${paramIndex}`; values.push(student_id); paramIndex++; } if (course_id) { query += ` AND g.course_id = $${paramIndex}`; values.push(course_id); paramIndex++; } if (semester) { query += ` AND g.semester = $${paramIndex}`; values.push(semester); paramIndex++; } if (min_score !== undefined) { query += ` AND g.score >= $${paramIndex}`; values.push(min_score); paramIndex++; } if (max_score !== undefined) { query += ` AND g.score <= $${paramIndex}`; values.push(max_score); paramIndex++; } query += ` ORDER BY g.created_at DESC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`; values.push(limit, offset); const client = await this.getClient(); try { const result = await client.query(query, values); return result.rows; } finally { client.release(); } } async getGradeById(id: number): Promise<GradeWithDetails | null> { const client = await this.getClient(); try { const result = await client.query(` SELECT g.*, s.name as student_name, s.student_id as student_id_code, c.course_name, c.course_code, c.teacher_name FROM grades g JOIN students s ON g.student_id = s.id JOIN courses c ON g.course_id = c.id WHERE g.id = $1 `, [id]); return result.rows[0] || null; } finally { client.release(); } } async createGrade(params: CreateGradeParams): Promise<Grade> { const client = await this.getClient(); try { const result = await client.query(` INSERT INTO grades (student_id, course_id, semester, score, grade, exam_date, remarks) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING * `, [ params.student_id, params.course_id, params.semester, params.score, params.grade, params.exam_date, params.remarks, ]); return result.rows[0]; } finally { client.release(); } } async updateGrade(id: number, params: UpdateGradeParams): Promise<Grade | null> { const client = await this.getClient(); try { const fields = Object.keys(params).filter(key => params[key as keyof UpdateGradeParams] !== undefined); if (fields.length === 0) return null; const setClause = fields.map((field, index) => `${field} = $${index + 2}`).join(', '); const values = [id, ...fields.map(field => params[field as keyof UpdateGradeParams])]; const result = await client.query(` UPDATE grades SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = $1 RETURNING * `, values); return result.rows[0] || null; } finally { client.release(); } } async deleteGrade(id: number): Promise<boolean> { const client = await this.getClient(); try { const result = await client.query('DELETE FROM grades WHERE id = $1', [id]); return (result.rowCount || 0) > 0; } finally { client.release(); } } // 统计方法 async getStudentGradeStats(studentId: number): Promise<StudentGradeStats | null> { const client = await this.getClient(); try { const result = await client.query(` SELECT s.id as student_id, s.name as student_name, s.student_id as student_id_code, s.class_name, COUNT(g.id) as total_courses, AVG(g.score) as average_score, SUM(c.credits) as total_credits, AVG(CASE WHEN g.grade = 'A+' THEN 4.3 WHEN g.grade = 'A' THEN 4.0 WHEN g.grade = 'A-' THEN 3.7 WHEN g.grade = 'B+' THEN 3.3 WHEN g.grade = 'B' THEN 3.0 WHEN g.grade = 'B-' THEN 2.7 WHEN g.grade = 'C+' THEN 2.3 WHEN g.grade = 'C' THEN 2.0 WHEN g.grade = 'C-' THEN 1.7 WHEN g.grade = 'D+' THEN 1.3 WHEN g.grade = 'D' THEN 1.0 WHEN g.grade = 'F' THEN 0.0 ELSE NULL END) as gpa FROM students s LEFT JOIN grades g ON s.id = g.student_id LEFT JOIN courses c ON g.course_id = c.id WHERE s.id = $1 GROUP BY s.id, s.name, s.student_id, s.class_name `, [studentId]); return result.rows[0] || null; } finally { client.release(); } } async getCourseGradeStats(courseId: number): Promise<CourseGradeStats | null> { const client = await this.getClient(); try { const result = await client.query(` SELECT c.id as course_id, c.course_name, c.course_code, c.teacher_name, COUNT(g.id) as total_students, AVG(g.score) as average_score, MAX(g.score) as highest_score, MIN(g.score) as lowest_score, (COUNT(CASE WHEN g.score >= 60 THEN 1 END) * 100.0 / COUNT(g.id)) as pass_rate FROM courses c LEFT JOIN grades g ON c.id = g.course_id WHERE c.id = $1 GROUP BY c.id, c.course_name, c.course_code, c.teacher_name `, [courseId]); return result.rows[0] || null; } finally { client.release(); } } } // 创建单例实例 const databaseService = new DatabaseService(); export default databaseService; 步骤4:初始化数据库在package.json的scripts中添加 "db:init": "tsx scripts/init-database.ts", 然后执行bun run db:init看到输出数据库初始化完成!即为成功初始化初始化后可以在华为云的数据库管理界面看到有三张表被创建了!3.6 编写业务代码步骤1:trpc路由创建app/lib/routers/course.tsimport { z } from 'zod'; import { createTRPCRouter, publicProcedure } from '../trpc'; import databaseService from '../../../lib/database'; export const courseRouter = createTRPCRouter({ // 获取所有课程 getAll: publicProcedure .query(async () => { return await databaseService.getCourses(); }), // 根据ID获取课程 getById: publicProcedure .input(z.number()) .query(async ({ input }) => { return await databaseService.getCourseById(input); }), // 创建课程 create: publicProcedure .input(z.object({ course_code: z.string(), course_name: z.string(), credits: z.number(), teacher_name: z.string().optional(), description: z.string().optional(), })) .mutation(async ({ input }) => { return await databaseService.createCourse(input); }), // 更新课程 update: publicProcedure .input(z.object({ id: z.number(), data: z.object({ course_name: z.string().optional(), credits: z.number().optional(), teacher_name: z.string().optional(), description: z.string().optional(), }), })) .mutation(async ({ input }) => { return await databaseService.updateCourse(input.id, input.data); }), // 获取课程成绩统计 getGradeStats: publicProcedure .input(z.number()) .query(async ({ input }) => { return await databaseService.getCourseGradeStats(input); }), }); 创建app/lib/routers/grade.tsimport { z } from 'zod'; import { createTRPCRouter, publicProcedure } from '../trpc'; import databaseService from '../../../lib/database'; export const gradeRouter = createTRPCRouter({ // 获取所有成绩 getAll: publicProcedure .input(z.object({ page: z.number().optional(), limit: z.number().optional(), student_id: z.number().optional(), course_id: z.number().optional(), semester: z.string().optional(), min_score: z.number().optional(), max_score: z.number().optional(), }).optional()) .query(async ({ input }) => { return await databaseService.getGrades(input); }), // 根据ID获取成绩 getById: publicProcedure .input(z.number()) .query(async ({ input }) => { return await databaseService.getGradeById(input); }), // 创建成绩 create: publicProcedure .input(z.object({ student_id: z.number(), course_id: z.number(), semester: z.string(), score: z.number().optional(), grade: z.string().optional(), exam_date: z.date().optional(), remarks: z.string().optional(), })) .mutation(async ({ input }) => { return await databaseService.createGrade(input); }), // 更新成绩 update: publicProcedure .input(z.object({ id: z.number(), data: z.object({ score: z.number().optional(), grade: z.string().optional(), exam_date: z.date().optional(), remarks: z.string().optional(), }), })) .mutation(async ({ input }) => { return await databaseService.updateGrade(input.id, input.data); }), // 删除成绩 delete: publicProcedure .input(z.number()) .mutation(async ({ input }) => { return await databaseService.deleteGrade(input); }), // 获取学生成绩 getByStudent: publicProcedure .input(z.object({ student_id: z.number(), semester: z.string().optional(), })) .query(async ({ input }) => { return await databaseService.getGrades({ student_id: input.student_id, semester: input.semester, }); }), // 获取课程成绩 getByCourse: publicProcedure .input(z.object({ course_id: z.number(), semester: z.string().optional(), })) .query(async ({ input }) => { return await databaseService.getGrades({ course_id: input.course_id, semester: input.semester, }); }), }); 创建app/lib/routers/student.tsimport { z } from 'zod'; import { createTRPCRouter, publicProcedure } from '../trpc'; import databaseService from '../../../lib/database'; export const studentRouter = createTRPCRouter({ // 获取所有学生 getAll: publicProcedure .input(z.object({ page: z.number().optional(), limit: z.number().optional(), search: z.string().optional(), class_name: z.string().optional(), gender: z.enum(['男', '女']).optional(), }).optional()) .query(async ({ input }) => { return await databaseService.getStudents(input); }), // 根据ID获取学生 getById: publicProcedure .input(z.number()) .query(async ({ input }) => { return await databaseService.getStudentById(input); }), // 根据学号获取学生 getByStudentId: publicProcedure .input(z.string()) .query(async ({ input }) => { return await databaseService.getStudentByStudentId(input); }), // 创建学生 create: publicProcedure .input(z.object({ student_id: z.string(), name: z.string(), gender: z.enum(['男', '女']), birth_date: z.date().optional(), class_name: z.string().optional(), phone: z.string().optional(), email: z.string().email().optional(), address: z.string().optional(), })) .mutation(async ({ input }) => { return await databaseService.createStudent(input); }), // 更新学生 update: publicProcedure .input(z.object({ id: z.number(), data: z.object({ name: z.string().optional(), gender: z.enum(['男', '女']).optional(), birth_date: z.date().optional(), class_name: z.string().optional(), phone: z.string().optional(), email: z.string().email().optional(), address: z.string().optional(), }), })) .mutation(async ({ input }) => { return await databaseService.updateStudent(input.id, input.data); }), // 删除学生 delete: publicProcedure .input(z.number()) .mutation(async ({ input }) => { return await databaseService.deleteStudent(input); }), // 获取学生成绩统计 getGradeStats: publicProcedure .input(z.number()) .query(async ({ input }) => { return await databaseService.getStudentGradeStats(input); }), }); 更新主路由文件app/lib/routers/_app.tsimport { createTRPCRouter } from '../trpc'; import { studentRouter } from './student'; import { courseRouter } from './course'; import { gradeRouter } from './grade'; export const appRouter = createTRPCRouter({ student: studentRouter, course: courseRouter, grade: gradeRouter, }); export type AppRouter = typeof appRouter; 步骤2:编写前端页面app/page.tsx'use client'; import { useState } from 'react'; import { trpc } from "../utils/trpc"; import Navigation from './components/Navigation'; import StudentManager from './components/StudentManager'; import CourseManager from './components/CourseManager'; import GradeManager from './components/GradeManager'; import ReportManager from './components/ReportManager'; function StatCard({ title, value, icon }: { title: string; value: string | number; icon: string }) { return ( <div className="bg-white rounded-lg shadow p-6"> <div className="flex items-center"> <div className="flex-1"> <p className="text-sm text-black">{title}</p> <p className="text-2xl font-bold text-black">{value}</p> </div> <div className="text-3xl">{icon}</div> </div> </div> ); } function SimpleTable({ title, data, columns }: { title: string; data: any[]; columns: { key: string; label: string }[]; }) { if (!data || data.length === 0) { return ( <div className="bg-white rounded-lg shadow p-6"> <h3 className="text-lg font-semibold mb-4 text-black">{title}</h3> <p className="text-black">暂无数据</p> </div> ); } return ( <div className="rounded-lg shadow p-6 bg-white"> <h3 className="text-lg font-semibold mb-4 text-black">{title}</h3> <div className="overflow-x-auto"> <table className="w-full"> <thead> <tr className="border-b"> {columns.map((col) => ( <th key={col.key} className="text-left py-2 px-4 font-medium text-black"> {col.label} </th> ))} </tr> </thead> <tbody> {data.slice(0, 5).map((row, index) => ( <tr key={index} className="border-b hover:bg-gray-50 text-black"> {columns.map((col) => ( <td key={col.key} className="py-2 px-4 text-black"> {row[col.key] || '-'} </td> ))} </tr> ))} </tbody> </table> </div> {data.length > 5 && ( <p className="text-sm text-gray-500 mt-2">显示前5条,共{data.length}条</p> )} </div> ); } export default function HomePage() { const [currentPage, setCurrentPage] = useState('dashboard'); const students = trpc.student.getAll.useQuery(); const courses = trpc.course.getAll.useQuery(); const grades = trpc.grade.getAll.useQuery({ limit: 10 }); const allGrades = trpc.grade.getAll.useQuery(); const totalStudents = students.data?.length || 0; const totalCourses = courses.data?.length || 0; const totalGrades = allGrades.data?.length || 0; const validGrades = allGrades.data?.filter(grade => grade.score !== null && grade.score !== undefined) || []; const avgScore = validGrades.length > 0 ? (validGrades.reduce((sum, grade) => sum + (parseFloat(String(grade.score)) || 0), 0) / validGrades.length).toFixed(1) : '0.0'; const showPage = () => { switch (currentPage) { case 'students': return <StudentManager />; case 'courses': return <CourseManager />; case 'grades': return <GradeManager />; case 'reports': return <ReportManager />; default: return ( <div className="space-y-6"> <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> <StatCard title="学生总数" value={totalStudents} icon="👥" /> <StatCard title="课程总数" value={totalCourses} icon="📚" /> <StatCard title="成绩记录" value={totalGrades} icon="📊" /> <StatCard title="平均分" value={avgScore} icon="⭐" /> </div> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <SimpleTable title="学生列表" data={students.data || []} columns={[ { key: 'name', label: '姓名' }, { key: 'student_id', label: '学号' }, { key: 'class_name', label: '班级' } ]} /> <SimpleTable title="课程列表" data={courses.data || []} columns={[ { key: 'course_name', label: '课程名称' }, { key: 'teacher_name', label: '教师' }, { key: 'credits', label: '学分' } ]} /> </div> <SimpleTable title="最新成绩" data={grades.data || []} columns={[ { key: 'student_name', label: '学生' }, { key: 'course_name', label: '课程' }, { key: 'score', label: '分数' }, { key: 'semester', label: '学期' } ]} /> </div> ); } }; return ( <div className="min-h-screen bg-gray-50"> <Navigation currentPage={currentPage} onPageChange={setCurrentPage} /> <main className="max-w-7xl mx-auto px-4 py-8"> {showPage()} </main> <footer className="bg-white border-t mt-12 py-6"> <div className="max-w-7xl mx-auto px-4 text-center text-gray-500 text-sm"> <p>学生成绩管理系统 作者dylan su</p> </div> </footer> </div> ); } app/components/Navigation.tsx'use client'; import { useState } from 'react'; interface NavigationProps { currentPage: string; onPageChange: (page: string) => void; } export default function Navigation({ currentPage, onPageChange }: NavigationProps) { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const menuItems = [ { id: 'dashboard', label: '仪表板' }, { id: 'students', label: '学生管理' }, { id: 'courses', label: '课程管理'}, { id: 'grades', label: '成绩管理' }, { id: 'reports', label: '统计报表'}, ]; return ( <nav className="bg-white shadow-sm border-b"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="flex justify-between h-16"> <div className="flex items-center"> <div className="flex-shrink-0 flex items-center"> <span className="text-xl font-bold text-gray-900">学生管理系统</span> </div> </div> <div className="hidden md:flex items-center space-x-8"> {menuItems.map((item) => ( <button key={item.id} onClick={() => onPageChange(item.id)} className={`flex items-center px-3 py-2 rounded-md text-sm font-medium transition-colors ${ currentPage === item.id ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:text-gray-900 hover:bg-gray-50' }`} > {item.label} </button> ))} </div> <div className="md:hidden flex items-center"> <button onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500" > <span className="sr-only">打开主菜单</span> {isMobileMenuOpen ? ( <svg className="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /> </svg> ) : ( <svg className="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" /> </svg> )} </button> </div> </div> </div> {isMobileMenuOpen && ( <div className="md:hidden"> <div className="px-2 pt-2 pb-3 space-y-1 sm:px-3 bg-white border-t"> {menuItems.map((item) => ( <button key={item.id} onClick={() => { onPageChange(item.id); setIsMobileMenuOpen(false); }} className={`flex items-center w-full px-3 py-2 rounded-md text-base font-medium transition-colors ${ currentPage === item.id ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:text-gray-900 hover:bg-gray-50' }`} > {item.label} </button> ))} </div> </div> )} </nav> ); } app/components/StudentManager.tsx'use client'; import { useState } from 'react'; import { trpc } from '../../utils/trpc'; export default function StudentManager() { const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState<number | null>(null); const [form, setForm] = useState({ student_id: '', name: '', gender: '男' as '男' | '女', class_name: '', phone: '', email: '' }); const students = trpc.student.getAll.useQuery(); const createStudent = trpc.student.create.useMutation({ onSuccess: () => { students.refetch(); resetForm(); } }); const updateStudent = trpc.student.update.useMutation({ onSuccess: () => { students.refetch(); resetForm(); } }); const deleteStudent = trpc.student.delete.useMutation({ onSuccess: () => students.refetch() }); const resetForm = () => { setForm({ student_id: '', name: '', gender: '男', class_name: '', phone: '', email: '' }); setEditingId(null); setShowForm(false); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (editingId) { updateStudent.mutate({ id: editingId, data: form }); } else { createStudent.mutate(form); } }; const handleEdit = (student: any) => { setEditingId(student.id); setForm({ student_id: student.student_id, name: student.name, gender: student.gender, class_name: student.class_name || '', phone: student.phone || '', email: student.email || '' }); setShowForm(true); }; const handleDelete = (id: number) => { if (confirm('确定删除这个学生?')) { deleteStudent.mutate(id); } }; return ( <div className="space-y-6 text-black"> <div className="flex justify-between items-center"> <h1 className="text-2xl font-bold">学生管理</h1> <button onClick={() => setShowForm(true)} className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600" > 添加学生 </button> </div> <div className="bg-white rounded-lg shadow p-6"> <h2 className="text-lg font-semibold mb-4">学生列表</h2> {students.isLoading ? ( <p>加载中...</p> ) : students.error ? ( <p className="text-red-500">加载失败</p> ) : ( <div className="overflow-x-auto"> <table className="w-full"> <thead> <tr className="border-b"> <th className="text-left py-2 px-4">学号</th> <th className="text-left py-2 px-4">姓名</th> <th className="text-left py-2 px-4">性别</th> <th className="text-left py-2 px-4">班级</th> <th className="text-left py-2 px-4">操作</th> </tr> </thead> <tbody> {students.data?.map((student) => ( <tr key={student.id} className="border-b hover:bg-gray-50"> <td className="py-2 px-4">{student.student_id}</td> <td className="py-2 px-4">{student.name}</td> <td className="py-2 px-4">{student.gender}</td> <td className="py-2 px-4">{student.class_name || '-'}</td> <td className="py-2 px-4"> <button onClick={() => handleEdit(student)} className="text-blue-600 hover:text-blue-800 mr-2" > 编辑 </button> <button onClick={() => handleDelete(student.id)} className="text-red-600 hover:text-red-800" > 删除 </button> </td> </tr> ))} </tbody> </table> </div> )} </div> {showForm && ( <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"> <div className="bg-white rounded-lg p-6 w-full max-w-md"> <h2 className="text-lg font-semibold mb-4"> {editingId ? '编辑学生' : '添加学生'} </h2> <form onSubmit={handleSubmit} className="space-y-4"> <div> <label className="block text-sm font-medium mb-1">学号</label> <input type="text" value={form.student_id} onChange={(e) => setForm({...form, student_id: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" required /> </div> <div> <label className="block text-sm font-medium mb-1">姓名</label> <input type="text" value={form.name} onChange={(e) => setForm({...form, name: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" required /> </div> <div> <label className="block text-sm font-medium mb-1">性别</label> <select value={form.gender} onChange={(e) => setForm({...form, gender: e.target.value as '男' | '女'})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" > <option value="男">男</option> <option value="女">女</option> </select> </div> <div> <label className="block text-sm font-medium mb-1">班级</label> <input type="text" value={form.class_name} onChange={(e) => setForm({...form, class_name: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" /> </div> <div> <label className="block text-sm font-medium mb-1">电话</label> <input type="text" value={form.phone} onChange={(e) => setForm({...form, phone: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" /> </div> <div> <label className="block text-sm font-medium mb-1">邮箱</label> <input type="email" value={form.email} onChange={(e) => setForm({...form, email: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" /> </div> <div className="flex gap-2 pt-4"> <button type="submit" className="flex-1 bg-blue-500 text-white py-2 rounded hover:bg-blue-600" disabled={createStudent.isPending || updateStudent.isPending} > {createStudent.isPending || updateStudent.isPending ? '保存中...' : '保存'} </button> <button type="button" onClick={resetForm} className="flex-1 bg-gray-500 text-white py-2 rounded hover:bg-gray-600" > 取消 </button> </div> </form> </div> </div> )} </div> ); } app/components/ReportManager.tsx'use client'; import { trpc } from '../../utils/trpc'; export default function ReportManager() { const students = trpc.student.getAll.useQuery(); const courses = trpc.course.getAll.useQuery(); const grades = trpc.grade.getAll.useQuery(); // 计算统计数据 const totalStudents = students.data?.length || 0; const totalCourses = courses.data?.length || 0; const totalGrades = grades.data?.length || 0; // 计算平均分 - 只计算有效的成绩,处理字符串类型的分数 const validGrades = grades.data?.filter(grade => grade.score !== null && grade.score !== undefined) || []; const avgScore = validGrades.length > 0 ? (validGrades.reduce((sum, grade) => sum + (parseFloat(String(grade.score)) || 0), 0) / validGrades.length).toFixed(1) : '0.0'; // 分数分布 - 只使用有效的成绩,处理字符串类型的分数 const scoreDistribution = validGrades.length > 0 ? { excellent: validGrades.filter(g => parseFloat(String(g.score)) >= 90).length, good: validGrades.filter(g => parseFloat(String(g.score)) >= 80 && parseFloat(String(g.score)) < 90).length, average: validGrades.filter(g => parseFloat(String(g.score)) >= 70 && parseFloat(String(g.score)) < 80).length, poor: validGrades.filter(g => parseFloat(String(g.score)) < 70).length } : { excellent: 0, good: 0, average: 0, poor: 0 }; return ( <div className="space-y-6 text-black"> <h1 className="text-2xl font-bold">统计报表</h1> <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> <div className="bg-white rounded-lg shadow p-6"> <div className="flex items-center"> <div className="flex-1"> <p className="text-sm text-black">学生总数</p> <p className="text-2xl font-bold text-black">{totalStudents}</p> </div> <div className="text-3xl">👥</div> </div> </div> <div className="bg-white rounded-lg shadow p-6"> <div className="flex items-center"> <div className="flex-1"> <p className="text-sm text-black">课程总数</p> <p className="text-2xl font-bold text-black">{totalCourses}</p> </div> <div className="text-3xl">📚</div> </div> </div> <div className="bg-white rounded-lg shadow p-6"> <div className="flex items-center"> <div className="flex-1"> <p className="text-sm text-black">成绩记录</p> <p className="text-2xl font-bold text-black">{totalGrades}</p> </div> <div className="text-3xl">📊</div> </div> </div> <div className="bg-white rounded-lg shadow p-6"> <div className="flex items-center"> <div className="flex-1"> <p className="text-sm text-black">平均分</p> <p className="text-2xl font-bold text-black">{avgScore}</p> </div> <div className="text-3xl">⭐</div> </div> </div> </div> <div className="bg-white rounded-lg shadow p-6"> <h2 className="text-lg font-semibold mb-4">分数分布</h2> <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> <div className="text-center"> <div className="text-2xl font-bold text-green-600">{scoreDistribution.excellent}</div> <div className="text-sm text-gray-600">优秀 (90+)</div> </div> <div className="text-center"> <div className="text-2xl font-bold text-blue-600">{scoreDistribution.good}</div> <div className="text-sm text-gray-600">良好 (80-89)</div> </div> <div className="text-center"> <div className="text-2xl font-bold text-yellow-600">{scoreDistribution.average}</div> <div className="text-sm text-gray-600">中等 (70-79)</div> </div> <div className="text-center"> <div className="text-2xl font-bold text-red-600">{scoreDistribution.poor}</div> <div className="text-sm text-gray-600">需努力 (&lt;70)</div> </div> </div> </div> <div className="bg-white rounded-lg shadow p-6"> <h2 className="text-lg font-semibold mb-4">课程平均分</h2> {courses.isLoading ? ( <p>加载中...</p> ) : ( <div className="space-y-3"> {courses.data?.map((course) => { const courseGrades = grades.data?.filter(g => g.course_id === course.id) || []; const avg = courseGrades.length > 0 ? (courseGrades.reduce((sum, g) => sum + (parseFloat(String(g.score)) || 0), 0) / courseGrades.length).toFixed(1) : '0.0'; return ( <div key={course.id} className="flex justify-between items-center py-2 border-b"> <span>{course.course_name}</span> <span className="font-semibold">{avg}分</span> </div> ); })} </div> )} </div> </div> ); } app/components/GradeManager.tsx'use client'; import { useState } from 'react'; import { trpc } from '../../utils/trpc'; export default function GradeManager() { const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState<number | null>(null); const [form, setForm] = useState({ student_id: 0, course_id: 0, score: 0, semester: '' }); const grades = trpc.grade.getAll.useQuery(); const students = trpc.student.getAll.useQuery(); const courses = trpc.course.getAll.useQuery(); const createGrade = trpc.grade.create.useMutation({ onSuccess: () => { grades.refetch(); resetForm(); } }); const updateGrade = trpc.grade.update.useMutation({ onSuccess: () => { grades.refetch(); resetForm(); } }); const deleteGrade = trpc.grade.delete.useMutation({ onSuccess: () => grades.refetch() }); const resetForm = () => { setForm({ student_id: 0, course_id: 0, score: 0, semester: '' }); setEditingId(null); setShowForm(false); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (editingId) { updateGrade.mutate({ id: editingId, data: form }); } else { createGrade.mutate(form); } }; const handleEdit = (grade: any) => { setEditingId(grade.id); setForm({ student_id: grade.student_id, course_id: grade.course_id, score: grade.score, semester: grade.semester }); setShowForm(true); }; const handleDelete = (id: number) => { if (confirm('确定删除这个成绩?')) { deleteGrade.mutate(id); } }; const getGradeLevel = (score: number) => { if (score >= 90) return { text: '优秀', color: 'text-green-600' }; if (score >= 80) return { text: '良好', color: 'text-blue-600' }; if (score >= 70) return { text: '中等', color: 'text-yellow-600' }; return { text: '需努力', color: 'text-red-600' }; }; return ( <div className="space-y-6 text-black"> <div className="flex justify-between items-center"> <h1 className="text-2xl font-bold">成绩管理</h1> <button onClick={() => setShowForm(true)} className="bg-purple-500 text-white px-4 py-2 rounded hover:bg-purple-600" > 录入成绩 </button> </div> <div className="bg-white rounded-lg shadow p-6"> <h2 className="text-lg font-semibold mb-4">成绩列表</h2> {grades.isLoading ? ( <p>加载中...</p> ) : grades.error ? ( <p className="text-red-500">加载失败</p> ) : ( <div className="overflow-x-auto"> <table className="w-full"> <thead> <tr className="border-b"> <th className="text-left py-2 px-4">学生</th> <th className="text-left py-2 px-4">课程</th> <th className="text-left py-2 px-4">分数</th> <th className="text-left py-2 px-4">等级</th> <th className="text-left py-2 px-4">学期</th> <th className="text-left py-2 px-4">操作</th> </tr> </thead> <tbody> {grades.data?.map((grade) => { const level = getGradeLevel(grade.score || 0); return ( <tr key={grade.id} className="border-b hover:bg-gray-50"> <td className="py-2 px-4">{grade.student_name}</td> <td className="py-2 px-4">{grade.course_name}</td> <td className="py-2 px-4">{grade.score}</td> <td className={`py-2 px-4 ${level.color}`}>{level.text}</td> <td className="py-2 px-4">{grade.semester}</td> <td className="py-2 px-4"> <button onClick={() => handleEdit(grade)} className="text-blue-600 hover:text-blue-800 mr-2" > 编辑 </button> <button onClick={() => handleDelete(grade.id)} className="text-red-600 hover:text-red-800" > 删除 </button> </td> </tr> ); })} </tbody> </table> </div> )} </div> {showForm && ( <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"> <div className="bg-white rounded-lg p-6 w-full max-w-md"> <h2 className="text-lg font-semibold mb-4"> {editingId ? '编辑成绩' : '录入成绩'} </h2> <form onSubmit={handleSubmit} className="space-y-4"> <div> <label className="block text-sm font-medium mb-1">学生</label> <select value={form.student_id} onChange={(e) => setForm({...form, student_id: parseInt(e.target.value)})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" required > <option value="">选择学生</option> {students.data?.map((student) => ( <option key={student.id} value={student.id}> {student.name} ({student.student_id}) </option> ))} </select> </div> <div> <label className="block text-sm font-medium mb-1">课程</label> <select value={form.course_id} onChange={(e) => setForm({...form, course_id: parseInt(e.target.value)})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" required > <option value="">选择课程</option> {courses.data?.map((course) => ( <option key={course.id} value={course.id}> {course.course_name} ({course.course_code}) </option> ))} </select> </div> <div> <label className="block text-sm font-medium mb-1">分数</label> <input type="number" min="0" max="100" value={form.score} onChange={(e) => setForm({...form, score: parseInt(e.target.value)})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" required /> </div> <div> <label className="block text-sm font-medium mb-1">学期</label> <input type="text" value={form.semester} onChange={(e) => setForm({...form, semester: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="如:2024春季" required /> </div> <div className="flex gap-2 pt-4"> <button type="submit" className="flex-1 bg-purple-500 text-white py-2 rounded hover:bg-purple-600" disabled={createGrade.isPending || updateGrade.isPending} > {createGrade.isPending || updateGrade.isPending ? '保存中...' : '保存'} </button> <button type="button" onClick={resetForm} className="flex-1 bg-gray-500 text-white py-2 rounded hover:bg-gray-600" > 取消 </button> </div> </form> </div> </div> )} </div> ); } app/components/CourseManager.tsx'use client'; import { useState } from 'react'; import { trpc } from '../../utils/trpc'; export default function CourseManager() { const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState<number | null>(null); const [form, setForm] = useState({ course_code: '', course_name: '', credits: 0, teacher_name: '', description: '' }); const courses = trpc.course.getAll.useQuery(); const createCourse = trpc.course.create.useMutation({ onSuccess: () => { courses.refetch(); resetForm(); } }); const updateCourse = trpc.course.update.useMutation({ onSuccess: () => { courses.refetch(); resetForm(); } }); const resetForm = () => { setForm({ course_code: '', course_name: '', credits: 0, teacher_name: '', description: '' }); setEditingId(null); setShowForm(false); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (editingId) { updateCourse.mutate({ id: editingId, data: form }); } else { createCourse.mutate(form); } }; const handleEdit = (course: any) => { setEditingId(course.id); setForm({ course_code: course.course_code, course_name: course.course_name, credits: course.credits, teacher_name: course.teacher_name || '', description: course.description || '' }); setShowForm(true); }; return ( <div className="space-y-6 text-black"> <div className="flex justify-between items-center"> <h1 className="text-2xl font-bold">课程管理</h1> <button onClick={() => setShowForm(true)} className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600" > 添加课程 </button> </div> <div className="bg-white rounded-lg shadow p-6"> <h2 className="text-lg font-semibold mb-4">课程列表</h2> {courses.isLoading ? ( <p>加载中...</p> ) : courses.error ? ( <p className="text-red-500">加载失败</p> ) : ( <div className="overflow-x-auto"> <table className="w-full"> <thead> <tr className="border-b"> <th className="text-left py-2 px-4">课程代码</th> <th className="text-left py-2 px-4">课程名称</th> <th className="text-left py-2 px-4">学分</th> <th className="text-left py-2 px-4">教师</th> <th className="text-left py-2 px-4">操作</th> </tr> </thead> <tbody> {courses.data?.map((course) => ( <tr key={course.id} className="border-b hover:bg-gray-50"> <td className="py-2 px-4">{course.course_code}</td> <td className="py-2 px-4">{course.course_name}</td> <td className="py-2 px-4">{course.credits}</td> <td className="py-2 px-4">{course.teacher_name || '-'}</td> <td className="py-2 px-4"> <button onClick={() => handleEdit(course)} className="text-blue-600 hover:text-blue-800" > 编辑 </button> </td> </tr> ))} </tbody> </table> </div> )} </div> {showForm && ( <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"> <div className="bg-white rounded-lg p-6 w-full max-w-md"> <h2 className="text-lg font-semibold mb-4"> {editingId ? '编辑课程' : '添加课程'} </h2> <form onSubmit={handleSubmit} className="space-y-4"> <div> <label className="block text-sm font-medium mb-1">课程代码</label> <input type="text" value={form.course_code} onChange={(e) => setForm({...form, course_code: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" required /> </div> <div> <label className="block text-sm font-medium mb-1">课程名称</label> <input type="text" value={form.course_name} onChange={(e) => setForm({...form, course_name: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" required /> </div> <div> <label className="block text-sm font-medium mb-1">学分</label> <input type="number" value={form.credits} onChange={(e) => setForm({...form, credits: parseInt(e.target.value)})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" required /> </div> <div> <label className="block text-sm font-medium mb-1">教师</label> <input type="text" value={form.teacher_name} onChange={(e) => setForm({...form, teacher_name: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" /> </div> <div> <label className="block text-sm font-medium mb-1">描述</label> <textarea value={form.description} onChange={(e) => setForm({...form, description: e.target.value})} className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" rows={3} /> </div> <div className="flex gap-2 pt-4"> <button type="submit" className="flex-1 bg-green-500 text-white py-2 rounded hover:bg-green-600" disabled={createCourse.isPending || updateCourse.isPending} > {createCourse.isPending || updateCourse.isPending ? '保存中...' : '保存'} </button> <button type="button" onClick={resetForm} className="flex-1 bg-gray-500 text-white py-2 rounded hover:bg-gray-600" > 取消 </button> </div> </form> </div> </div> )} </div> ); } 3.6 运行项目执行bun dev然后看到终端返回打开http://localhost:3000即可看到项目运行的状况4.1 页面展示4.1.1仪表盘这是学生管理系统的仪表板页面,作为系统的主控制面板,它通过四个关键数据卡片(学生总数6人、课程总数10门、成绩记录10条、平均分77.0)快速展示系统整体状况,同时以表格形式分别列出了学生基本信息、课程设置和最新成绩记录,让管理员能够一目了然地掌握当前教学管理的核心数据和最新动态。4.1.2 学生管理这是学生管理系统的学生管理页面,专门用于学生信息的维护和管理。页面顶部设有"添加学生"按钮用于新增学生记录,主体部分以表格形式展示了6名学生的详细信息,包括学号(2024001-2024006)、姓名、性别和所属班级,每行末尾都提供"编辑"和"删除"操作按钮,方便管理员对学生信息进行实时的增删改查操作。4.1.3 课程管理这是学生管理系统的课程管理页面,用于管理学校的课程信息。页面顶部提供"添加课程"按钮用于新增课程,主体部分以表格形式展示了10门课程的详细信息,包括:核心课程:数学、语文、英语、物理、化学、生物等主要学科每门课程都配有相应的任课教师和学分设置课程信息包含:课程代码(如MATH101、CHI101等标准化编码)课程名称学分(2.0-4.0学分不等)任课教师编辑操作按钮4.1.4 成绩管理这是学生管理系统的成绩管理页面,用于记录和管理学生的考试成绩。页面功能包括:主要功能录入成绩:页面右上角提供"录入成绩"按钮,用于添加新的成绩记录成绩查看:以表格形式展示所有学生的成绩信息成绩信息展示页面显示了3名学生(张三、李四、王五)在2024春季学期的各科成绩:学生成绩概况:张三:语文92分(优秀)、英语78.5分(中等)、数学85.5分(良好)李四:数学95分(优秀)、语文88.5分(良好)、英语91分(优秀)王五:数学72分(中等)、语文85分(良好)、英语79.5分(中等)每条成绩记录都提供"编辑"和"删除"操作,方便教师对成绩进行修改和维护。整个界面设计清晰,便于教务人员进行成绩管理工作。4.1.5 统计报表5.1总结本项目基于华为开发者空间的云开发环境,采用 Bun + Next.js + tRPC 现代化全栈技术栈,成功构建了一个功能完善的学生成绩管理系统。系统包含课程管理和成绩管理两大核心模块,实现了标准化的课程编码体系和科学的成绩评价机制。通过 Bun 超快运行时提升构建效率,Next.js 处理前后端复杂逻辑,tRPC 确保端到端类型安全,原生 SQL 直接对接华为 GaussDB 数据库实现最优查询性能。项目充分利用华为开发者空间提供的免费数据库实例和完整开发工具链,体验了从开发编码到应用调测的全流程云开发模式。系统界面简洁直观,支持课程信息维护、成绩录入编辑、智能等级评定等功能,为学校教务管理提供了高效便捷的数字化解决方案。通过实际开发实践,深入了解了现代全栈开发最佳实践和华为根技术生态的强大能力,为未来进一步集成昇腾AI、鸿蒙等技术打下了坚实基础。至此本案例关于在开发者空间–远程开发环境中部署Bun + Next.js + tRPC与开发者空间生态版GaussDB实践操作并编写学生成绩管理系统完毕。我正在参加【案例共创】第6期 开发者空间-基于云开发环境和GaussDB构建应用(https://bbs.huaweicloud.com/forum/thread-0229189398343651003-1-1.html)
  • [热门活动] 奖品已寄出//【开发者空间推荐官】第一期:推荐+案例实践最高可得千元好礼
    华为开发者空间推荐官第一期活动可兑奖名单如下,请获奖的小伙伴在11月4日前反馈收件信息(逾期反馈不再补发),报名用户积分统计明细请查看附件清单。华为云账号名hw0****3314huy****34hw3****dan****jikongzhongxinhid****7_ovlxw7pv8mjac****bithhid****1ifa25zm3vwohid****oebixpzcsk1tGT-****0161443hw0****3976hw8****549lic****qian1207hid****20xjdev_3p9phid****ez52-g4tnneahw_****15102517719_01hw5****jos****yhid****8jbsk12d0e78hid****_zbcinmpv181GT-****5655215CSD****_35655215hid****m9v6qo05banrhid****69lei-l9eypqhid****35zv6a1s9uv-hid****9_o479gzk8poCCI****luo****hid****xmbj5f41hwr3mav****kshwfre****yGT-****_zhang_zhanzhu****clin****hw8****062h_v****9802063Rob****iuyshw_****18844196063_01mql****16hid****p8u1_k84yq4zhw_****17671701081_01a18****60983hid****gqha3hgwtdirhw6****927 开发者空间推荐官第一期来啦~成功邀请好友报名/开通开发者空间/参与空间案例实践,可获得不同积分激励,累计最高可兑换千元开发者定制好礼+千元代金券,不仅有华为音箱、耳机、手环,更有开发者双肩包、冲锋衣、云宝盲盒等好礼,快叫上小伙伴一起来参加吧~ 【活动时间】2025年8月27日-9月30日 【活动流程】① 完成报名 →  ② 分享专属推广链接  →  ③ 邀请好友报名 / 开通开发者空间 / 参与空间案例实践 → ④ 积分兑换激励ps:报名成功后一定要从“分享有礼”获取自己专属推广链接哦~    【案例参考】可以选择下方任意1个案例进行实践或独立完成开发者空间云主机相关的开发,实践完成后请在此帖下方评论,评论需包含案例名称+案例截图+使用感受,活动结束后,小编会对有真实使用记录的同学给与积分激励技术场景阶段实践案例难度系数AI 入门云主机调用DeepSeek实现代码自动生成★★本地部署DeepSeek构建RAG向量数据库★★★基于DeepSeek搭建Agent智能助手★★★进阶自主编程之Cline×DeepSeek的智能融合探索★★★基于TensorFlow的手写体识别★★★基于PyTorch的手写体识别★★★场景开发开发者空间部署Cherry Studio+高德地图MCP Server构建出行规划助手★★★基于华为开发者云主机部署FastGPT并构建知识库智能体★★★FastGPT问答系统实战:知识库检索×联网搜索★★★基于开发者空间通过调用MySQL MCP Server实现对外部数据源的获取等实操★★★软件开发入门基于云主机的CodeArts IDE运行Java电商项目★基于CodeArts Repo云仓库管理云主机代码★进阶基于CodeArts Build的项目容器化构建★★★场景开发CodeArts IDE调用API实现电商平台问答助手★★使用云主机CodeArts IDE进行远程JVM服务调试★★ 【积分规则说明】积分项目积分数积分发放说明活动报名1分/人邀请者和被邀请者参加活动报名,可各积1分开通开发者空间2分/人每新邀请一个好友新开通空间,则邀请者和被邀请者各积2分参与空间案例实践5分/人完成空间案例实践或独自完成空间应用构建,在论坛评论区回帖案例名称+使用截图,核实真实有效后可积5分/人 【礼品兑换说明】序号积分兑换规则可兑换礼品(云资源代金券和实物礼品可同时获得)可兑换数量兑换前提条件云资源代金券实物礼品价值110分>积分>1分20元云资源代金券开发者定制鼠标垫(大号)200推荐开通开发者空间数≥2或完成空间案例实践220分>积分≥10分30元云资源代金券50元实物礼品100推荐开通开发者空间数≥3或完成空间案例实践330分>积分≥20分50元云资源代金券100元实物礼品30440分>积分≥30分100元云资源代金券200元实物礼品15推荐开通开发者空间数≥5或完成空间案例实践560分>积分≥40分200元云资源代金券300元实物礼品5推荐开通开发者空间数≥8或完成空间案例实践680分>积分≥60分300元云资源代金券500元实物礼品27100分>积分≥80分500元云资源代金券800元实物礼品1推荐开通开发者空间数≥12或完成空间案例实践8积分>100分800元云资源代金券1000元实物礼品1推荐开通开发者空间数≥20或完成空间案例实践1、活动结束后,按积分高低排名依次兑换,部分礼品兑换数量有限,必须足额积分后且满足兑换前提条件下再填写兑换问卷,否则兑奖无效。若高兑换档位名额已空,则按积分高低自动往下占用低档位兑换名额;反之若高兑换名额无人兑换,则低兑换名额会自动扩充。2、实物礼品包括华为无线耳机、体脂称、手环9、云宝礼盒、开发者定制冲锋衣、定制短袖等,如遇缺货,将随机发货,部分礼品实物图如下~  【活动说明】用户限制说明:1、参加本次社区活动的用户必须为华为云注册用户。同时为保证活动公平性,禁止用户以IAM账号身份参与活动,否则将视为无效。2、领取奖品的用户需为华为云实名用户,未完成实名认证的用户将不发放对应活动奖励。3、本次活动如一个实名认证对应多个账号,只有一个账号可领取奖励。如在同一概率活动中,同一账号重复获奖,只发放首先获奖奖品。4、本次活动一个实名认证账号只能对应一个收件人,如同一账号填写多个不同收件人,不予发放奖励。5、请开发者不要在活动期间随意修改社区昵称和华为云账号,由此产生的统计问题,如过了申诉期,小助手不再处理。(申诉期为活动结果公示3天内。)奖品发放说明:1、本活动结束之后15个工作日内公示获奖信息,获奖开发者用户需在截止时间在获奖信息收集表中填写获奖信息,获奖信息截止收集日过后10个工作日内,将统一发出实物奖品和云资源代金券。华为云遵守《中华人民共和国个人信息保护法》规定,将以上个人信息仅用于礼品发放之目的,不会向任何第三方披露。若由于获奖开发者用户自身原因(包括但不限于联系方式有误、身份不符或超过截止登记日期等)造成奖品无法发送,视为获奖开发者用户放弃领奖。2、为保证活动的公平公正,华为云有权对恶意刷活动资源(“恶意”是指为获取资源而异常注册账号等破坏活动公平性的行为),利用资源从事违法违规行为的开发者用户收回抽奖及奖励资格。3、若发放实物奖品时出现库存不足,则优先发放等价值的其他实物奖品;云资源券领取有效期为60天,领取后有效使用时间为1年内,逾期未使用不予补发4、所有参加本活动的开发者用户,均视为认可并同意遵守《华为云开发者用户协议》,包括以援引方式纳入《华为云开发者用户协议》、《可接受的使用政策》、《法律声明》、《隐私政策声明》、相关服务等级协议(SLA),以及华为云服务网站规定的其他协议和政策(统称为“云服务协议”)的约束。 
  • [产品体验官] 【开发者体验活动】友好性开发者招募第二期:免费领取云数据库GaussDB在线试用版,快来体验吧!
    活动介绍:为让云服务产品更精准贴合实际场景、更全面覆盖使用需求,我们特发起本次友好开发者招募活动。无论你是熟悉业务的技术达人,还是刚接触产品的新手用户,只要你有想法、有经验,都欢迎加入进来 —— 分享你的使用心得、补充未覆盖的场景细节、提出内容优化建议。一旦您的建议经过专家评审团的认可与采纳,将会获得开发者定制礼品!我们期待着与您一起,共同打造更加优质、高效的云服务体验。活动时间2025.8.23-2025.9.30活动流程:01 领取云数据库GaussDB在线试用版02 按照实践案例深度使用体验03 在活动贴下跟帖评论运行结果04 提交开发者工单,反馈产品优化建议第1步:实验前的准备实名登录:(已注册并实名可跳过)华为云账号实名认证,点击这里。(已设置可跳过)登录后设置社区昵称,点我设置。第2步:领取云数据库GaussDB在线试用版:(1)     点击申请链接:cid:link_2(2)     填写申请信息:(3)     欢迎加入微信群交流:  第3步:动手操作吧 (1)       实践案例查看地址:cid:link_3(2)       完成体验后截图在评论区(3)       任务过程中,遇到操作问题可在本微信群进行交流(4)       遇到产品功能问题,请提交问题工单(必须是开发者工单)(5)       完成问卷调查。奖品设置奖项设置获奖要求获奖名额激励礼品建议排名奖被采纳建议数≥3条5每人价值200元开发者礼包1份高价值建议奖被评选为高价值需求5每人价值200元开发者礼包1份说明:1.      每条被采纳建议累计1积分,被采纳建议数统计截止时间为2025年9月30日24点;2.      奖品数量有限,按照时间先后顺序,先到先得;3.      如礼品库存不足将存在替换成等价值礼品的可能;4.      开发者礼包举例:序号礼包名称介绍1200元开发者礼包1开发者定制双肩包(黑武士款)开发者定制渔夫帽(2025款)开发者定制鼠标垫大号(2025款)2200元开发者礼包2开发者定制冲锋衣(M-3XL)开发者定制渔夫帽(2025款)开发者定制鼠标垫大号(2025款)活动说明(1)       建议提交时需要在标题中以“【GaussDB体验】”为建议标题开头,比如【GaussDB体验】建议增加XX/优化XX/XX操作失败等。(2)       建议内容仅针对云数据库GaussDB应用实践活动,非该活动的建议内容不参与此活动。(3)       提交的建议要求建议对云产品功能及优化改进有积极作用,建议内容需要表述清晰,有明确的建议方案,最好有操作截图或链接等能进一步详细描述。(4)       若出现积分相同且排名一致的情况,根据先到先得原则进行发放。(5)       同一用户在同一页面(文档)提出的同一类用户体验问题(包括但不限于错别字、语句不通顺、视觉体验等),在被采纳后仅算作一条积分。(6)       兑换礼品以仓库现有礼品为准,不可以指定,如遇商品缺货,将随机换成其他等价值礼品发放。
  • [分享交流] 2025年华为全连接大会马上开始了,你最期待什么内容呢??欢迎回帖交流
    2025年华为全连接大会马上开始了,你最期待什么内容呢??欢迎回帖交流  
  • [案例共创] 【案例共创】基于华为云开发者云主机创建部署 AdonisJS 与华为云数据库 GaussDB 的实践应用
    案例介绍本案例选择 AdonisJS 作为示例,并借助开发者空间云开发环境提供的免费 GaussDB 数据库和 HCE2.0 开发环境进行本地部署 AdonisJS 生态组件、轻松部署上云案例内容1). 概述1.1 概述AdonisJS:现代 Node.js 全栈框架A. 核心定位    专为高效开发企业级 Node.js 应用设计的全功能框架,采用 MVC 架构,对标 Laravel/Rails 的开发体验。B. 核心优势    a.一体化开发        内置 ORM(Lucid)、认证系统、验证器、邮件服务        原生支持 TypeScript,强类型保障    b.高性能架构        异步 IOC 容器管理依赖        多进程 HTTP 服务器优化    c.开发者友好        CLI 工具自动生成控制器/模型/迁移文件        文件路由系统(start/routes.ts)    d.安全防护        原生 CSRF 保护        加密会话/Cookie 管理        SQL 注入防护(ORM 参数化查询)适用场景:API 服务、全栈 Web 应用、实时应用华为云 GaussDB:企业级分布式数据库A.核心定位    华为自主研发的金融级云原生分布式数据库,兼容 PostgreSQL/MySQL 生态,主打高可靠与极致性能。B.核心能力    a.金融级可靠性        RTO<30秒,RPO=0(故障秒级切换)        多地多中心容灾架构    b.极致性能        分布式并行计算(MPP)        向量化执行引擎提速 10x    c.深度云集成        存储计算分离架构        秒级扩缩容能力    d.生态兼容        完全兼容 PostgreSQL 协议        支持 AdonisJS Lucid ORM 无缝接入典型场景:金融核心系统、政企数仓、工业物联网当 AdonisJS ✖️ GaussDB:✅ 开发提效:Lucid ORM 原生支持 GaussDB 驱动✅ 成本优化:按需使用云数据库计算节点✅ 安全合规:双端中国企业级安全认证✅ 性能保障:Node.js 事件循环 + 分布式执行引擎尤其适合国产化替代项目及高并发数字业务系统构建。1.2 适用对象企业个人开发者高校学生1.3 案例流程① 用户打开华为开发者空间云主机;② 浏览器下载 VSCode,完成安装配置;③ Adonis 服务 与 华为云数据库 GaussDB 的结合;④ Adonis 服务开发及部署;1.4 资源总览华为云免费开发者空间–云主机华为开发者空间 - 生态版GaussDB弹性公网 IP ( 需要一定的花费 )2). 环境准备2.1 领取云主机并进入云主机桌面面向广大开发者群体,华为开发者空间提供一个随时访问的“开发桌面云主机”、丰富的“预配置工具集合”和灵活使用的“场景化资源池”,开发者开箱即用,快速体验华为根技术和资源。如果还没有领取开发者空间云主机,可以参考免费领取云主机文档领取。领取云主机后可以直接进入华为开发者空间工作台界面,点击进入桌面连接云主机。2.2 领取 GaussDB 数据库免费领取GaussDB在线试用版(2025年 06月 21日 - 2025年 12月 31日)。华为开发者空间-GaussDB云数据库领取与使用指导。注意:部署的Django项目需要对接GaussDB,因此GaussDB需要绑定EIP,参考上述指导中领取部分第(5)步2.3 安装 VSCode直接打开浏览器,进入 VSCode 下载官网: https://code.visualstudio.com/Download然后根据自己的机型选择需要下载的版本即可,如果跟笔者机器一样的话,直接选择图上 .deb 下载即可下载完成之后一般都会在 /home/developer/Downloads直接左下角打开终端执行以下命令:sudo dpkg -i /home/developer/Downloads/code_xxx.debxxx 为版本号,如果不清楚的话,可以直接输入到 /code_ 然后按下 Table 键即可tip:笔者这是已经安装过了,所以有些不一样也无所谓,只要不报错即可…如果出现安装依赖报错的话可以执行以下命令尝试修复:sudo apt install -f安装完成后,执行以下命令:code2.4 安装 Nodejs 、 npm系统虽然自带了 Nodejs 但是笔者看着好像版本略低,好像是 14.x,所以建议直接更新到最新版本。可以直接去 Nodejs 官网进行下载,或者打开终端执行以下命令:# Download and install nvm: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash # Download and install Node.js: nvm install node # Verify the Node.js version: node -v # Should print "24.5.0". # Verify npm version: npm -v # Should print "11.5.1". 至此,环境安装完成了,接下来我们就可以进行项目构建3). 构建项目3.1 更换 npm 源首先我们的第一件事,就是先把 npm 的源给换了,因为 npm 官方源可能会被墙,导致速度下载速度很慢,并且下载失败,报错的几率比较大,所以我们可以换成国内的镜像源。执行以下命令即可换源,我们现在换的是华为云,如果有需要其他 npm 源可以自行百度,更换即可:npm config set registry https://mirrors.huaweicloud.com/repository/npm/ 3.2 创建 AdonisJS 项目在终端输入以下命令:npm init adonisjs@latest my-app然后他会告诉你Need to install the fllowing packages: create-adonisjs@xxx输入 y 回车即可等待下载,需要等待一小段时间,如果时间太长的话可以 Ctrl + C 断开重新输入之前的命令然后重新下载…安装成功后根据自己所需的来选择 Starter KitSlim starter kitFor minimalists, we have created a slim starter kit. It comes with just the core of the framework and the default folder structure. You may use it when you do not want any bells and whistles of AdonisJS.Web starter kitThe Web starter kit is tailored for creating traditional server renderer web apps. Do not let the keyword “traditional” discourage you. We recommend this starter kit if you make a web app with limited frontend interactivity.The simplicity of rendering HTML on the server using Edge.js will boost your productivity as you do not have to deal with complex build systems to render some HTML.Later, you can use Hotwire, HTMX, or Unpoly to make your applications navigate like an SPA and use Alpine.js to create interactive widgets like a dropdown or a modal.API starter kitThe API starter kit is tailored for creating JSON API servers. It is a trimmed-down version of the web starter kit. If you plan to build your frontend app using React or Vue, you may create your AdonisJS backend using the API starter kitInertia starter kitInertia is a way to build server-driven single-page applications. You can use your favorite frontend framework ( React, Vue, Solid, Svelte ) to build the frontend of your application.You can use the --adapter flag to choose the frontend framework you want to use. The available options are react, vue, solid, and svelte.You can also use the --ssr and --no-ssr flags to turn server-side rendering on or off.选择完毕之后等待脚手架安装、配置环境即可…这可能会需要一段时间…到这里为止,项目就创建完成了!3.3 项目调试在终端输入 code 打开 VS Code选择 File -> Open Folder…然后找到刚刚创建的项目 my-app ,或者是你们自己填的项目名他会在当前目录下创建一个文件夹,选择他点击打开即可勾选 Trust the authors of all files in the parent folder ‘developer’选择 Yes, I trust the authors然后关掉右侧的 Copilot点击顶部管理栏中的省略号( 或者是直接显示 Terminal )然后选择 New Terminal 新建终端这样新开的终端就会在当前文件夹中打开这样可以更好的对项目进行调试然后输入以下命令运行项目npm run dev出现上图则证明服务启动成功了,打开浏览器输入 localhost:3333 即可看到网页!3.4 配置华为云数据库 GaussDB进入 GaussDB云数据库点击实例 进入实例详情,查看公网 ip,以及数据库端口,然后在终端输入以下命令telnet $EIP $PORT $EIP 是数据库绑定的公网 ip$PORT 为数据库端口因为笔者已经把数据库端口改成了 5432 所以我这里填的是 5432,如果没有改的话应该是 8000 好像,请注意分辨噢!而且! 如果 telnet 没有反应的话,请检查安全组是否把服务器端口放行!!!先把华为云数据库 GaussDB 参数 password_encryption_type 的值 改为 1这步一定要在创建用户之前完成!因为目前笔者找不到 sha256 连接数据库的方法...所以先把参数改成 1 支持 md5 进行登录 使用 root@postgres 登录数据库实例,并且在 sql 查询中输入以下命令create user xiaoxia with password 'huawei@123'; create database adonis owner xiaoxia; GRANT ALL PRIVILEGES ON DATABASE adonis to xiaoxia ; ALTER ROLE xiaoxia CREATEDB; 创建用户,数据库成功后,左边选择库名:adonisSchema:public进入到 adoins 数据库中然后在执行以下 sql 创建表 departmentsCREATE TABLE departments ( department_id INTEGER NOT NULL, department_name CHAR(3), manager_id INTEGER, location_id INTEGER ); 点击左边的刷新按钮就会出现表 departments再执行以下 sql 来给表插入数据,方便后续测试insert into departments(department_id,department_name,manager_id,location_id) values (10,'Adm',200,1700),(20,'Mar',201,1800),(30,'Pur',114,1700),(40,'Hum',203,2400),(50,'Shi',121,1500),(60,'IT',103,1400),(70,'Pub',204,2700),(80,'Sal',145,2500),(90,'Exe',100,1700),(100,'Fin',108,1700),(110,'Acc',205,1700),(120,'Tre',NULL,1700),(130,'Cor',NULL,1700),(140,'Con',NULL,1700),(150,'Sha',NULL,1700),(160,'Ben',NULL,1700),(170,'Man',NULL,1700),(180,'Con',NULL,1700),(190,'Con',NULL,1700),(200,'Ope',NULL,1700),(210,'IT ',NULL,1700),(220,'NOC',NULL,1700),(230,'IT ',NULL,1700),(240,'Gov',NULL,1700),(250,'Ret',NULL,1700),(260,'Rec',NULL,1700),(270,'Pay',NULL,1700); 数据插入成功之后输入GRANT USAGE ON SCHEMA public TO xiaoxia; GRANT SELECT ON departments TO xiaoxia; SELECT * FROM departments查看表数据是否插入成功,如果呈现下图所示证明插入成功!用户名 xiaoxia 、密码 huawei@123、数据库名adonis 以及表名 departments 可以自己决定这个之后会在 AdoinsJS 中用到3.5 Adonis 项目连接华为云数据库打开 VS Code 然后编辑文件 config/databse.tsconst dbConfig = defineConfig({ connection: 'postgres', connections: { postgres: { client: 'pg', version: '7.4', connection: { host: env.get('DB_HOST'), port: env.get('DB_PORT'), user: env.get('DB_USER'), password: env.get('DB_PASSWORD'), database: env.get('DB_DATABASE'), }, migrations: { naturalSort: true, paths: ['database/migrations'], }, }, }, }) 加上 version: ‘7.4’因为不加的话,版本过新,导致他内部的方法 跟 华为云数据库返回的数据没能做到完全兼容,导致报错。再找到目录下 .env 文件TZ=UTC PORT=3333 HOST=localhost LOG_LEVEL=info APP_KEY=1iLDxooWc3Or_kxZ4YJx05On-2ftAXZ9 NODE_ENV=development SESSION_DRIVER=cookie DB_HOST=139.9.223.20 DB_PORT=5432 DB_USER=xiaoxia DB_PASSWORD=huawei@123 DB_DATABASE=adonis注意更改 DB_HOST、DB_PORT、DB_USER、DB_PASSWORD、DB_DATABASE这些都是之前设置的,如果跟笔者不一样的话请填写 3.4 中创建的用户名等信息再最后加上测试路由,编辑 start 文件夹下的 routes.ts 文件import db from '@adonisjs/lucid/services/db' router.get('/test-db', async () => { try { const response = await db.rawQuery('SELECT * FROM departments') return { status: 'success', message: '✅ 数据库连接成功!', details: 'GaussDB 连接正常', response } } catch (error) { return { status: 'error', message: '❌ 数据库连接失败', error: error.message, solution: '请检查以下配置:1. 主机地址 2. 端口 3. 用户名/密码 4. SSL设置' } } }) 注意 sql SELECT * FROM departments 中表名 departments如果跟笔者不一样的话请更改,参考 3.4更改文件记得使用 Ctrl + s 保存文件!更改完文件之后回到浏览器输入 localhost:3333/test-db就可以看到以下输出从图上可以看出,我们已经成功连接了数据库,并且能够查询出数据!3.6 报错浏览器返回报错status "error" message "❌ 数据库连接失败" error "SASL: Only mechanism(s) SCRAM-SHA-256 are supported" solution "请检查以下配置:1. 主机地址 2. 端口 3. 用户名/密码 4. SSL设置" 如果出现以下报错! 就证明,你选择登录的用户是在修改参数 password_encryption_type 为 1 之前创建的!所以 可以先把 password_encryption_type 修改为 1 然后再重新创建用户 并且给予用户权限,再把 .env 里边的 DB_USER DB_PASSWORD 改成后创建的用户即可!控制台报错/home/developer/my-app/node_modules/pg/lib/query.js:145 throw err ^ TypeError: Cannot read properties of null (reading '1') at Client_PG._parseVersion (/home/developer/my-app/node_modules/knex/lib/dialects/postgres/index.js:135:56) at Query.<anonymous> (/home/developer/my-app/node_modules/knex/lib/dialects/postgres/index.js:129:22) at Query.handleReadyForQuery (/home/developer/my-app/node_modules/pg/lib/query.js:142:14) at Client._handleReadyForQuery (/home/developer/my-app/node_modules/pg/lib/client.js:321:19) at Connection.emit (node:events:507:28) at /home/developer/my-app/node_modules/pg/lib/connection.js:116:12 at Parser.parse (/home/developer/my-app/node_modules/pg-protocol/src/parser.ts:103:9) at Socket.<anonymous> (/home/developer/my-app/node_modules/pg-protocol/src/index.ts:7:48) at Socket.emit (node:events:507:28) at addChunk (node:internal/streams/readable:559:12) Node.js v24.5.0 如果出现以上报错则证明在 confog 文件夹下的 database.ts 文件,并没有添加上 version: ‘7.4’,版本过新,导致与华为云数据库并没有完全兼容!把 version: ‘7.4’ 添加上即可4). 项目上线4.1 前言该章节为可选章节,完成这个章节可以把自己的 AdonisJS 项目部署在服务公网上,可以让广大网友进行访问,但是需要注意的是需要到一台有公网 IP 的服务器,具体可以看看华为云 Flexus 应用服务器 L 实例,新用户性价比还是比较高的。!不建议使用前三节进行学习用到的免费华为云开发者云主机!4.2 使用 FinalShell 或者其他工具对服务器进行 SSH 连接其他工具:XShell 等笔者在该章节用 FinalShell 做演示工具,可以自行下载,或者更换其他工具,都大差不差打开 FinalShell 点击上方 Tab 栏的文件夹按钮点击 添加连接 按钮,然后选择 SSH 连接输入信息必要信息,主机 (公网IP) 由 4.1 图中所得,一般创建服务器的时候都会叫你创建一个密码以用于 SSH 连接linux 系统端口一般为 22用户名一般为 root4.3 打包 AdonisJS 项目并上传到服务器在华为云开发者空间的机器 VS Code 中,输入 npm run build 对项目进行打包出现上图字眼则证明打包成功然后使用 scp 把 build 文件夹上传到服务器scp -r ./build root@$EIP:~ $EIP 为服务器的公网 ip然后在回到 FinalShell 并且输入以下命令cd build npm ci --omit="dev" tip: 执行该命令之前,请在自己的云服务器中安装 Nodejs、Npm (章节 2.5) 以及设置好国内 Npm 源(章节 3.1)看到以下字眼则证明依赖安装成功:added 97 packages, and audited 98 packages in 32s 20 packages are looking for funding run `npm fund` for details found 0 vulnerabilities4.4 配置环境变量在 FinalShell 中执行以下命令修改配置环境变量nano ~/.bashrc 把下面的文字添加到文件的末尾export TZ=UTC export PORT=3333 export HOST=0.0.0.0 export LOG_LEVEL=info export APP_KEY=1iLDxooWc3Or_kxZ4YJx05On-2ftAXZ9 export NODE_ENV=production export SESSION_DRIVER=cookie export DB_HOST=139.9.223.20 export DB_PORT=5432 export DB_USER=xiaoxia export DB_PASSWORD=huawei@123 export DB_DATABASE=adonis注意!APP_KEY 请根据自己项目中 .env 中的 APP_KEY 进行填写!以及如果 DB_HOST DB_USER DB_PASSWORD DB_DATABASE 与笔者不一致请参考 3.4添加完成之后使用 Ctrl + x 退出然后输入 y在输入回车 确认最后再执行 source ~/.bashrc 来启用刚刚设置的环境变量没有问题之后输入以下命令启动 AdonisJS 服务node bin/server.js出现以下字眼则证明服务启动成功啦!{"level":30,"time":1755165680553,"pid":1904355,"hostname":"ECS9021","msg":"started HTTP server on 0.0.0.0:3333"} 然后打开浏览器输入 公网IP:3333 得到跟之前一样的页面!看起来没有任何问题当前案例到这里就完全结束啦!5. 总结构建了基于AdonisJS的现代化Web应用,采用华为云全栈方案:计算层:部署于华为开发者空间云主机(ECS),提供弹性计算资源数据层:集成华为云GaussDB(分布式云数据库),支撑高性能数据存储应用层:使用AdonisJS全功能框架(Node.js)开发通过npm run build编译生产版本配置环境变量实现服务监听0.0.0.0网络架构:云主机绑定弹性公网IP安全组放行3333端口公网访问核心优势:✅ 华为云生态深度整合✅ GaussDB提供金融级数据可靠性(兼容PostgreSQL)✅ AdonisJS开箱即用的身份认证/ORM功能✅ 云主机SSD磁盘保障I/O性能典型工作流:本地开发 → 构建编译 → 云主机部署 → GaussDB数据交互 → 公网用户访问此架构兼顾开发效率与生产稳定性,适合中大型企业级应用。正在参加【案例共创】第6期 开发者空间-基于云开发环境和GaussDB构建应用 https://bbs.huaweicloud.com/forum/thread-0229189398343651003-1-1.html
  • [热门活动] HCDG城市行·长沙站|华为云MaaS大模型即服务平台实践技术沙龙圆满收官!
          2025年8月8日,华为云MaaS大模型即服务平台实践技术沙龙在九云科教集团成功举办。本次活动由华为云HCDG长沙核心组周毅、黄蓉发起,来自于北京并行科技、北京惠农通正、杭州泰师科技、湖南中科博远、湖南拓维信息、湖南科创信息等企业的30余位人工智能领域开发者齐聚一堂,活动围绕当前热门的MaaS平台展开深入讨论,共谋AI大模型的应用与发展。       长沙九云信息科技有限公司董事长高宏祥发表致辞,高度赞赏华为作为中国科技领军企业的担当精神,特别提到其自主创新的鸿蒙系统、昇腾AI等突破性成果。他希望与会开发者把握AI时代机遇,依托华为云等开放平台,共同探索智能教育、行业大模型等创新应用,推动AI生态高质量发展。       华为云DTSE技术专家分享了华为云在AI大模型和数字化转型的成功实践和技术积累,介绍了如何使用ModelArts Studio一站式大模型开发平台完成模型的训推与部署,结合平台提供的模型API、MCP、应用模板快速构建应用,实现行业应用的高效落地,并与企业开发者们深度探讨了平台底层能力。       华为云DTSE高级工程师在活动中,带领大家在华为开发者空间中体验了《基于DeepSeek和Dify构建心理咨询师应用》的案例,此案例使用Dify对接华为云MaaS平台,成功连接到基于昇腾训练的DeepSeek大模型,实现了心理咨询师应用的功能。这个案例的实操,让开发者了解到华为开发者空间为开发者提供的高效、稳定的开发环境,同时华为云MaaS平台作为Dify内置模型供应商,对接更加便捷。       华为云战略合作伙伴——金蝶软件的院校高级顾问杨名先生分享了“智联云途:华为云携手金蝶云苍穹探索智能时代新路径”主题演讲。金蝶云苍穹ERP深度融合华为云AI平台,通过技术共生、场景共创、生态共享,正在重塑企业数字化转型的路径。从底层架构的自主可控,到行业场景的深度渗透,再到开发者生态的繁荣,双方不仅为企业提供 “开箱即用” 的智能解决方案,更通过持续的技术创新与模式探索,引领智能时代的产业变革。这种 “云+端+AI”的协同模式,将成为推动中国企业数智化升级的核心引擎。       华为云伙伴——网久软件联合创始人&COO周建军先生分享了“基于华为云部署开源组件的方法”主题演讲,Websoft9 多应用托管与运维平台可以通过华为云商店一键购买并创建ECS实例,自动完成部署。支持多应用托管、可视化运维及资源扩展,10分钟内即可上线。       在集中讨论环节中,与会企业围绕MaaS平台、Agent开发技术、AI人才培养等展开深度交流。大家一致认为,MaaS平台通过提供预训练模型和低代码工具,可显著降低企业AI应用开发门槛;2025年被视为Agent技术商业化落地的关键元年,各行业都在积极探索智能体在业务流程优化、客户服务等场景的应用价值;而针对AI人才培养问题,与会代表建议通过校企合作建立实训基地,并开发体系化培训课程,重点培养既懂AI技术又熟悉行业应用的复合型人才,以支撑企业智能化转型需求。       华为云高级技术专家毛定宇为九云科教集团的产品总监周毅和运维部经理黄蓉,授予华为云开发者组织HCDG长沙站旗帜。这对于中部地区的开发人员来说是一个重要里程碑时刻,再次感谢长沙HCDG核心组成员们对于本次活动的辛勤付出和认真负责,相信长沙HCDG会在以后举办出更多有价值有意义的精彩活动、赋能越来越多的开发者。       HCDG(Huawei Cloud Developer Group 华为云开发者社区组织),是基于城市圈和技术圈,由开发者核心组自发开展的开放、创新、多元的社区技术交流组织。致力于帮助开发者学习提升、互动交流、挖掘合作,推动技术应用与本地产业结合、数智化转型和开发者文化发展。迄今为止,越来越多的社区和组织开始广泛的与HCDG进行密切的合作与往来,相信在今后的日子里,HCDG会以更加崭新的面貌出现在开发者视野中,我们期待越来越多优秀的开发者、创业者、运营人、产品人、老师、学生等加入到HCDG,让HCDG茁壮成长、遍地开花、节节高!
  • [版主交流] 【华为云社区外部版主】2025年7月激励评比结果已公布!
    各位亲爱的版主们,大家好!经过大家一个月的努力角逐,7月外部版主激励评比结果已出炉,数据公示如下,请查看!(在新标签页打开图片可查看清晰大图/见附件)·外部版主激励规则:点击了解更多转正礼/基础任务/额外任务(在线时长15小时+,主题帖15+,回帖30+,技术长文5+/原创技术干货1+,合集1+,有效回复问题求助帖10+,话题互动1+,完成这4项指标可获对应价值的代金券/实物礼品)请完成任务获得激励的版主,点击填写激励发放意愿统计问卷反馈截止时间:2025年8月17日,以便小编进行相应的激励发放。注:在线时长数据达标后,才会再去考察达标版主的三项任务完成情况;主题数+回帖数达标后,才会再去考察达标版主的技术长文数量情况。
  • [热门活动] 【中奖名单公示】///【产品体验官】华为开发者空间云开发环境震撼升级,提优化建议领千元开发者好礼
    【华为开发者空间云开发环境】产品体验官活动获奖名单如下:一、有效建议奖:昵称建议分值奖项礼品yd_2846377506.5有效建议奖第一名1000元开发者定制礼品miyalian5.5有效建议奖第二名800元开发者定制礼品yd_2388226594有效建议奖第三名500元开发者定制礼品二、优质建议奖:昵称礼品给无眠点压力200元开发者定制礼品yd_213866132200元开发者定制礼品yd_256610494200元开发者定制礼品小草飞上天200元开发者定制礼品lycchee200元开发者定制礼品yd_232023009200元开发者定制礼品风吹雨200元开发者定制礼品yd_212578646200元开发者定制礼品banjin200元开发者定制礼品cxw200元开发者定制礼品 恭喜以上11名获奖用户,请获奖用户通过以下问卷反馈奖品收件信息(9月25日前反馈有效),感谢大家对云声平台的关注和支持~ 华为开发者空间云开发环境功能正式上线啦!华为开发者空间云开发环境三大核心优势:1、一键创建,零门槛上手:无需复杂配置,只需登录华为开发者空间,点击 “新建开发环境”,填写名称即可完成创建;2.全生命周期管理,灵活又省心:随用随启、按需暂停、自动续时、安全删除;3.无缝对接工具链,开发体验无差异:支持通过 Xshell、CodeArts IDE、VSCode、JetBrains、Notebook等主流工具远程连接,本地工具怎么用,云端环境就怎么操作;大家赶快来体验吧,体验完后提交开发者空间优化建议,还可以领取开发者礼包,包括但不限于华为耳机、手环、鼠标、云宝等好礼,快叫上小伙伴一起提建议吧~ 【实践项目】体验项目项目名称难度系数功能体验开发平台:云开发环境/案例体验开发者空间 - 云开发环境使用指导 ★★★开发者空间云开发环境使用指导-Linux★★★本地Xshell基于华为开发者空间云开发环境完成上传下载 ★★★本地IntelliJ IDEA 基于华为开发者空间云开发环境的应用开发  ★★★本地CodeArts IDE基于华为开发者空间云开发环境完成小游戏开发★★★ 本地VSCode基于华为开发者空间云开发环境完成小程序开发★★★基于华为开发者空间-云开发环境,部署Jupyter Notebook★★★ 基于华为开发者空间-云开发环境,PyCharm SSH远程开发环境搭建 ★★★ 基于远程开发环境部署Django与开发者空间GaussDB的实践应用 ★★★★基于开发者空间编写ODBC应用程序操作GaussDB数据库  ★★★ 基于华为开发者空间云开发环境部署Coze Studio + Maas构建智能体应用 ★★★★详细信息请见“开发平台”,案例中心。 【活动时间】2025年8月5日-9月5日 【参与方式】01 体验开发者空间开发平台   》   02 去云声平台提建议    》  03 建议评估公示  》   04 获奖公示(活动结束后两周内) ps:建议标题需要以“开发者空间体验官”开头 【奖项设置】奖项设置评选条件获奖名额激励礼品有效建议奖1、有效建议数量不少于2条,有效建议由内部技术专家评审得出2、建议内容需针对上述实践项目3、有效建议中有不低于一条是关于功能体验的3名积分榜第1名:1000元礼品/人积分榜第2名:800元礼品/人积分榜第3名:500元礼品/人优质建议奖1、优质有效建议不少于1条,由内部技术专家评审选出10名每人200元开发者定制礼品【活动说明】1、建议预审通过即为有效建议,其中1条有效功能类建议为1分,1条有效体验类建议为0.5分,1条有效Bug类建议0.5分;有效建议奖与优质建议奖不可叠加2、建议提交时需要在标题中以“【开发者空间体验官】”为建议标题开头,比如【开发者空间体验官】开发者空间增加XX/优化XX/导入XX等3、建议内容仅针对以上实践项目中“开发平台功能体验+开发平台案例体验”涉及的云产品,非以上实践项目涉及产品建议内容不参与此活动4、优质建议要求建议对云产品功能及优化改进有重要作用,优先从已被采纳的建议的选择;建议内容需要表述清晰,有明确的建议方案,最好有操作截图或链接等能进一步详细描述;高价值建议数量不限,且与有效建议奖可叠加,每位用户每月最多可获得一次。5、注意事项1)若出现积分相同且排名一致的情况,结合已实现和已采纳建议情况,由内部技术专家选出价值更高的建议用户给予奖励2)同一用户在同一页面(文档)提出的同一类用户体验问题(包括但不限于错别字、语句不通顺、视觉体验等),在通过审核后仅算作一条有效建议数3)若发现代他人提交优化建议,此建议分值只取原分值30%;若发现2次及以上重复提交他人建议,或3次及以上重复提交体验类相关建议进行恶意刷量(包括但不限于错别字、语句不通顺、视觉体验等),取消本人活动参与资格;在活动截止日前进行大量刷屏提交建议,或者重复提交同类型体验建议,直接取消活动参与资格4)以上兑换礼品均为仓库现有实物礼品,有货的情况下优先满足,其中1-3名可优先选择华为自营品牌电子礼品,礼品价值不能超过商品原价,无货则不可以指定,如遇商品缺货,将随机换成其他等价值礼品发放5)此【开发者空间体验官】与云声月度例行激励活动不叠加,若标题无【开发者空间体验官】标记,则有效建议默认参与月度例行激励
  • [热门活动] 奖品已邮寄//【案例实践】体验华为开发者空间云开发环境,更多惊喜好礼等你解锁
    本期华为开发者空间【案例实践】活动获奖名单如下,请获奖用户9月25日前反馈收件信息,奖品将于10月上旬寄出  活动一【体验打卡】获奖清单活动二【实践互动】获奖清单——————————————————————————————————————————————————————————————期待已久的华为开发者空间云开发环境正式上线啦~小伙伴们是不是已经跃跃欲试中?允许小编再次介绍下云开发环境的三大能力,①一键创建,零门槛上手②全生命周期管理,灵活又省心③无缝对接工具链,开发体验无差异本期【案例实践】欢迎各位开发者沉浸式体验云开发环境能力,从使用指导 → 到环境搭建→应用开发,全方位案例手把手教学,更有华为手环、HDC定制大礼包、开发者双肩包等好礼等你解锁~【活动时间】8月1日—8月31日 【活动流程】完成报名 → 选择活动方式 → 参与活动 → 获得激励 【活动方式】(以下活动均可参与,奖励可叠加)活动一【体验打卡】:体验打卡开发者空间云开发环境功能,参与开发者定制礼品抽奖参与方式:进入开发者空间,体验空间云开发环境新能力奖项设置:开发者定制双肩包*2个,开发者定制短袖T恤*10件 活动二【实践互动】:完成开发者空间“云开发环境”新功能案例体验或独立开发与“云开发环境”的相关的新案例,抽取华为手环等好礼参与方式:选择下方任意一个或多个案例或独立完成与“云开发环境”相关的案例进行体验,并在评论区分享案例截图(也欢迎书写你对案例实践后的体验和感受),活动结束后,将在评论区用户中抽奖。体验功能案例名称难度系数云开发环境开发者空间 - 云开发环境使用指导★★★本地Xshell基于华为开发者空间云开发环境完成上传下载★★★基于华为开发者空间-云开发环境,PyCharm SSH远程开发环境搭建★★★基于华为开发者空间-云开发环境,部署Jupyter Notebook★★★★本地VSCode基于华为开发者空间云开发环境完成小程序开发★★★本地CodeArts IDE基于华为开发者空间云开发环境完成小游戏开发★★★本地IntelliJ IDEA 基于华为开发者空间云开发环境的应用开发★★★基于远程开发环境部署Django与开发者空间GaussDB的实践应用★★★★奖项设置:华为手环9(NFC款)*2个,HDC定制大礼包*3个(内含HDC双肩包、渔夫帽、小风扇及短袖T恤),开发者定制短袖T恤*15件中奖小妙招:完成多个案例或者独自开发完成新案例,会增加中奖概率!完成后请在回帖中注明完成了几个案例,以及案例名称。小编会对案例完成情况进行复核,请大家真实完成哦! 部分礼品示意PS:能独立完成新案例开发的小伙伴也可以参加开发者空间案例共创第六期活动,如在使用中有产品优化建议,可参与开发者空间产品体验官活动,更多礼品等你解锁~【活动规则】1、抽奖方式:活动结束后,我们将从参与活动的用户中(华为云新老用户均可参与),通过巨公平台或Excel 函数形式抽取获奖用户。获奖名单将在活动结束后的7个工作日内公布。2、活动二仅限于在“华为开发者空间”内体验相关案例项目,其他项目建议不参与此次活动,否则视为无效内容,具体参考案例中心内容。另外活动将根据有效评论数设置获奖人员数量,规则如下:有效评论数量获奖名额1052010302040以上全部开启3、本次活动将根据实际参与情况发放奖励,包括但不限于用户百分之百中奖或奖项轮空的情况。【活动说明】用户限制说明:1、参加本次社区活动的用户必须为华为云注册用户。同时为保证活动公平性,禁止用户以IAM账号身份参与活动,否则将视为无效。2、领取奖品的用户需为华为云实名用户,未完成实名认证的用户将不发放活动奖励。3、本次活动如一个实名认证对应多个账号,只有一个账号可领取奖励。如在同一概率活动中,同一账号重复获奖,只发放首先获奖奖品。4、本次活动一个实名认证账号只能对应一个收件人,如同一账号填写多个不同收件人,不予发放奖励。5、请开发者不要在活动期间随意修改社区昵称和华为云账号,由此产生的统计问题,如过了申诉期,小助手不再处理。(申诉期为活动结果公示3天内。)奖品发放说明:1、本活动结束之后10个工作日内公示获奖信息,获奖开发者用户需在截止时间在获奖信息收集表中填写获奖信息,获奖信息截止收集日过后10个工作日内,将统一发出奖品。华为云遵守《中华人民共和国个人信息保护法》规定,将以上个人信息仅用于礼品发放之目的,不会向任何第三方披露。若由于获奖开发者用户自身原因(包括但不限于联系方式有误、身份不符或超过截止登记日期等)造成奖品无法发送,视为获奖开发者用户放弃领奖。2、为保证活动的公平公正,华为云有权对恶意刷活动资源(“恶意”是指为获取资源而异常注册账号等破坏活动公平性的行为),利用资源从事违法违规行为的开发者用户收回抽奖及奖励资格。3、若发放奖品时,出现库存不足,则优先发放等价值的其他实物奖品;HDC限定礼包为24/25年款随机发货;4、所有参加本活动的开发者用户,均视为认可并同意遵守《华为云开发者用户协议》,包括以援引方式纳入《华为云开发者用户协议》、《可接受的使用政策》、《法律声明》、《隐私政策声明》、相关服务等级协议(SLA),以及华为云服务网站规定的其他协议和政策(统称为“云服务协议”)的约束。5、如果您不同意本活动规则和云服务协议的条款,请勿参加本活动。
  • 【🎁快来领礼品啦🎁】填问卷,领礼品,快来展现你的智慧吧,了解您在使用控制台过程中的真实体验和需求~~
    您好!诚邀您参加华为云控制台的体验调研问卷,我们希望更深入了解您在使用过程中的真实体验和需求。您的反馈将帮助我们更好地改进产品,为您带来更优质的体验。 【参与方式】您可选择您感兴趣的议题 ,进入对应链接即可参与:🔍 控制台资源管理与查找体验调研:cid:link_0🔍 控制台运维体验调研:cid:link_1🔍 控制台配置与跨服务操作体验调研:cid:link_2 【调研奖励】我们将随机选取 150名 认真填答的用户,发放调研激励金。请务必认真作答,抽奖仅从有效问卷中抽取!!!  非常欢迎您给华为云控制台提建议,我们会认真评估每一条建议! 【隐私声明】为开展调研获取您对华为产品和服务的意见和建议,华为将处理您提供的个人数据(包括姓名、电话号码)用于与您发送问卷。您的个人数据将被严格保密。 有关如何处理您的数据的更多信息,请参阅隐私政策: 《华为客户及合作伙伴满意度调查隐私声明》https://www.huawei.com/cn/privacy-policy
  • [分享交流] 深圳人才福利!5大“国家队”级信创技术免费培训课程开始报名
    当别人已在鸿蒙生态接单月入3万+,你还在Java内卷?当大模型重构IT岗位,传统运维正批量淘汰…体贴的深圳人社为您再次奉上信创技术/鸿蒙系统/麒麟系统等新一代信息应用技术生存技能大餐•       信创智算与大模型技术课程•       开源高斯数据库技术课程•       鸿蒙原生应用开发课程•       开源鸿蒙设备应用技术课程•       前沿  科技  国产系统应用 还等什么基于麒麟操作系统的信创基础软件适配迁移与运维课程 【理论授课、现场实战、组队攻坚】告别枯燥的理论推砌聘请行业专家担任讲师5门免费课程助你抢占信息技术高地,满级晋升!最重要的是全!免!费!咱不花一分钱就开启成长与蜕变的大门报名有啥要求?咋报名?快随我往下看吧!一、  报名条件报名学员需具有新一代电子信息应用技术相关的行业从业背景或具备相关专业背景,并满足以下条件之一即可:1.本市户籍人员;2.在本市正常缴交社会保险的人员;3.深圳市登记失业人员;4.在深圳市公共就业服务机构进行离校两年未就业实名登记的本市高校毕业生;5.本市高校或本市户籍在市外高校的毕业年度(指毕业时间在2025年1月1日至12月31日之间)毕业生(含技师学院高级工班、预备技师班)。温馨提示:(1)同一劳动者同一年度只能参加1次项目制培训哦。如果您已经参加2024年度项目制培训但未完成规定学时50%以上,很遗憾,那无法参加2025年度的项目制培训了哦。(2)同一劳动者同一年度内企业新型学徒制培训,学生学徒制培训、技培生学徒制培训只能参加一次,且均不能和项目制培训同时享受。等等先别急还没完!还有额外补贴!【额外补贴】如您满足以下条件之一:•       本市就业困难人员•       本市零就业家庭成员•       本市就业残疾人•       本市城市低保家庭成员•       本市毕业2年内的“两后生”中的农村学员•       本市求职就业的脱贫人员不仅可以免费学课程还可以再领500元的生活补贴金💴! 接下来咱们看一下具体学习内容吧!二、  学习内容以及报名方式 新一代电子信息应用技术项目制培训 指导单位:深圳市人力资源和社会保障局主办单位 :深圳市职业技能培训指导中心承办单位:深圳职业技术大学1、信创智算与大模型技术课程课程内容培训天数培训名额主要内容包括:1.基于昇腾平台的 DeepSeek 模型搭建与优化2.华为云昇腾算力支持下的私有场景大模型部署3.基于昇腾与 DeepSeek 的私有大模型自主训练4.电商场景下大模型的创新应用与拓展6天约300人 信创智算与大模型技术课程报名二维码及交流群(QQ群)    2、开源高斯数据库技术课程课程内容培训天数培训名额主要内容包括:1.高斯数据库安装与对象管理实操2.场景化高斯数据库实验探索3.数据库AI策略与技巧4.数据安全管理与防护6天约250人开源高斯数据库技术课程报名二维码及交流群(QQ群)   3、      鸿蒙原生应用开发课程课程内容培训天数培训名额主要内容包括:1.基于ArkTSUI框架搭建实训云平台2.鸿蒙原生办公签到系统开发3.基于Next版本开发实时社交软件联动DeepSeek实现聊天问答4.基于鸿蒙服务卡片开发音乐推荐软件5.鸿蒙原生健康服务检测软件开发6天约250人 鸿蒙原生应用开发课程报名二维码及交流群(QQ群)     4、      开源鸿蒙设备应用技术课程课程内容培训天数培训名额主要内容包括:1.OpenHarmony搭建与配置2.开源鸿蒙设备驱动开发和集成3.基于开源鸿蒙的HAL层开发4.基于开源鸿蒙的智能家居软硬件开发6天约250人 开源鸿蒙设备应用技术课程报名二维码及交流群(QQ群)    5、基于麒麟操作系统的信创基础软件适配迁移与运维课程课程内容培训天数培训名额主要内容包括:1.银河麒麟桌面操作系统 V10 的管理应用2.掌握适配测试基础及软硬件适配测试技能6天约250人 基于麒麟操作系统的信创基础软件适配迁移与运维课程报名二维码及交流群(QQ群)   三、成长与收获1.掌握实用技能,提高自身职业技能,增强就业竞争力,优化职业发展路径;2.培训考勤达标且考核通过将获得《深圳市职业技能提升培训合格证书》;3.可自行选考行业权威认证:HCIA-AI认证、HCIA-openGauss认证、HarmonyOS应用开发者认证、开放原子OpenHarmony人才认证、KYCA、KYCP认证(不含考证费)。四、班级设置l  7月29日-9月30日  开设日常精品班(周一到周六)开设周末精品班(周六或周日单日班)l  7月29日-10月20日开设周末精品班(周六或周日单日班)五、咨询与联络黄老师:13528095312(微信同号)周老师:0755-26019607咨询时间:工作日9:00- 18:00其他时间咨询联系QQ群工作人员六、培训地点深圳职业技术大学西丽湖园区(信息楼)建议绿色出行:深圳地铁5号线西丽地铁站F口步行800米。公交车站-深圳职业技术大学(西丽湖园区),线路包括M197、M182、M176、M492、高峰专线59、325、M535、M217、67、326等。 别再犹豫,抓住这个难得的机会,让自己在公益性培训中实现华丽转身!立即报名,开启你的成长之旅!
  • [案例共创] 【案例共创】基于华为开发者空间快速实现一个智能家居园艺助手
    案例介绍在华为开发者空间部署AI Agent服务完成一个智能家居园艺助手,基于华为云开发者空间提供的免费入AI Agent界面开通后进行功能实现,充分利用大模型的智能知识问答能力。案例内容1 概述1.1 案例介绍在数字化与智能化日益渗透我们生活的今天,园艺这一传统而充满生机的领域,也迎来了智能化的革新。华为AI Agent智能园艺助手通过融合人工智能与园艺专业知识,为用户提供一站式、个性化的园艺养护解决方案,彻底改变了传统园艺的养护模式。使用AI Agent进行家居园艺专业领域知识库构建,包含进行组织、存储及管理知识和利用知识开展问答活动。过程非常简单易学,下面我将带领大家一同构建! 华为开发者空间AI Agent:AI原生应用引擎提供企了包括数据准备、模型选择/调优、知识工程、模型编排、应用部署、应用集成等能力,降低智能应用开发门槛、提升开发效率。华为开发者空间的AI Agent集成Versatile空间,Versatile空间是通用型AI助手和领域专家Agent的智能协同工作空间,通过自主任务规划及多工具协同,实现复杂任务的智能化处理,从而提升工作效率。 1.2 适用对象企业个人开发者高校学生1.3 案例时间本案例总时长预计40分钟。1.4 案例流程① 用户打开领取链接领取并开通服务;② 在Versatile配置页面完成创建Agent;③ 添加知识库进行知识增强提升回答准确性(可选);④添加MCP提升连续思索等能力(可选);⑤ 完成发布应用。 1.5 资源总览本案例预计花费0元。 2 环境配置2.1 领取并开通服务开发者进入开发者空间时,若是首次进入AI Agent界面,此时功能默认是未开通的状态,需要首先点击开通,之后会自定进行开通初始化。领取链接:cid:link_0 点击“开通服务”,完成开通后进行下一步操作。在开发平台的Agent界面点击“创建Agent”按钮。   2.2 创建Agent然后进入了Versatile(AI原生应用引擎)可进行创建Agent。如下只需要输入基础信息。名称栏填写“智能家居园艺助手”描述栏填写“帮我智能化解答家庭种植常见问题”。角色设定点击对应方框右上角的智能修改小图标就可以自动生成。同理,完成对话设置中的“开场白”和“推荐问题”。模型选择DeepSeek-R1-32K。  可以在Agent预览界面输入任意和家居园艺主题相关的问题进行询问体验,示范问题如下: “请告诉我家里种绿萝盆栽的注意事项”  以下为增强配置操作(可选):2.3 添加知识库(可选)为本模型增加知识库进行RAG增强,使得回答更加精准。点击知识-知识库右侧“+”号展开创建知识库界面。   选择创建知识库并确认,然后会进入创建知识库配置界面如下。 如上图设置知识库名称为:“植物养护知识大全”。 RAG类型为“VectorRAG”.数据来源为“接入源数据”数据类型选择“文档”数据接入方式选择本地上传。然后点击“文件上传”按钮进行文件上传。如下所示知识库请根据需求自备(本环节为可选环节)。 完成后点击“保存并启用”按钮便完成知识库的创建了。然后在刚刚的Versatile主界面直接再次点击添加知识库“+”,这次我们选择现有知识库。   选择刚刚创建的“植物养护知识大全”进行添加。 2.4 创建MCP服务(可选)创建Agent,点击创建MCP服务按钮进行MCP创建。由于我们所需MCP服务没有默认模板,所以可以自行到官网进行复制后配置。  进入添加MCP界面后,点击创建MCP。    然后右上角点击创建MCP服务。 基于空白模板进行创建。 然后输入参数即可完成安装。服务名称:SequentialThinking服务描述: 为模型提供连续思考能力。安装方式:NPX参数修改为:-y,@modelcontextprotocol/server-sequential-thinking点击“安装”即可完成配置。  然后会在我的MCP列表中显示刚才设置的MCP,初始显示安装中,2-3分钟左右即可完成。 2.5 发布应用配置完成后发布即可使用。  发布渠道勾选上Web Url设置发布秘钥,初次需点击“获取API KEY”进行获取。如下点击“新增平台API Key”,输入名称后确定。  将会自动完成api key的excel文本下载,本地打开csv文件即可查询到API Key。 将其粘贴到API Key对应位置即可。点击右上角“发布”按钮即可完成发布。  再点击右上角完成发布。  此时会自动返回开发平台。我的资产处可以看到应用已成功发布。   分享打开链接可以体验如下:   至此,基于华为开发者空间快速实现一个智能家居园艺助手到此到此结束。 本案例Web访问地址:https://appstage.huaweicloud.com/wiseagent/orchestration/agent?id=b76d96a1c4ad42ee9df9954c24a61331&isWeb=T&isExperiencing=true&api=/v2/routes/app-b76d96a1c4ad42ee9df9954c24a61331/b76d96a1c4ad42ee9df9954c24a61331/execute 我正在参加【案例共创】第5期 开发者空间 AI Agent 开发  cid:link_1 
  • [分享交流] 华为云CloudMatrix 384超节点获WAIC“镇馆之宝”,300PFlops算力如何改变AI产业格局?超节点能否打破英伟达垄断?
    华为在WAIC 2025首次线下展出的昇腾384超节点,凭借384颗昇腾NPU+192颗鲲鹏CPU的全对等互联架构,实现300PFlops算力,单卡推理吞吐达2300Tokens/s!超节点能否打破英伟达垄断?
  • [专题汇总] FAQ—2025年7月份论坛问题求助合集
    CodeArts能否配置租户域名cid:link_0gaussdb对列约束无法生效cid:link_2GaussDB 在分布式架构下如何保证跨节点事务的 ACID 特性cid:link_3GaussDB 声称支持 OLTP 和 OLAP 混合负载,但其底层存储引擎如何同时优化 行存(高并发事务) 和 列存(分析查询)cid:link_4GaussDB 为适配国产化环境(如鲲鹏 CPU、麒麟 OS)做了哪些 指令集优化 和 内核态调整?cid:link_1gaussdb执行计划中出现的BYPASS字样,该如何理解和处理cid:link_5在华为云上部署GaussDB时,如何利用存算分离架构实现弹性扩展?自动扩缩容的触发条件是什么cid:link_6GaussDB提供了哪些企业级安全功能?如数据加密、动态脱敏、细粒度权限控制等如何配置?cid:link_7GaussDB对PostgreSQL和Oracle的语法兼容性达到什么程度?迁移现有Oracle应用到GaussDB需要哪些适配工作cid:link_8GaussDB的分布式事务处理是如何实现的?与传统单机数据库相比有哪些性能优势? cid:link_9如果想把 Oracle/SQL Server 数据库迁移到 GaussDB,但是又有大量复杂的存储过程怎么办cid:link_10云数据库GaussDB如何配置SSL认证cid:link_11