乐于分享
好东西不私藏

《姓铭堂人类大学APP》全套代码架构(下)

《姓铭堂人类大学APP》全套代码架构(下)

《姓铭堂人类大学APP》全套代码架构(下)

三、前端核心代码示例

3.1 React Native 主界面

javascript

// App.js - 主应用入口import React,{ useEffect, useState }from'react';import{ NavigationContainer }from'@react-navigation/native';import{ createNativeStackNavigator }from'@react-navigation/native-stack';import{ Provider }from'react-redux';import store from'./store';import*as Font from'expo-font';// 导入屏幕import HomeScreen from'./screens/HomeScreen';import LearningScreen from'./screens/LearningScreen';import SocialScreen from'./screens/SocialScreen';import MetaverseScreen from'./screens/MetaverseScreen';import GrowthTreeScreen from'./screens/GrowthTreeScreen';import ProfileScreen from'./screens/ProfileScreen';const Stack =createNativeStackNavigator();exportdefaultfunctionApp(){const[fontsLoaded, setFontsLoaded]=useState(false);useEffect(()=>{asyncfunctionloadFonts(){await Font.loadAsync({'Chinese-Calligraphy':require('./assets/fonts/Chinese-Calligraphy.ttf'),'Noto-Sans-SC':require('./assets/fonts/Noto-Sans-SC.ttf'),});setFontsLoaded(true);}loadFonts();},[]);if(!fontsLoaded){return<LoadingScreen />;}return(<Provider store={store}><NavigationContainer><Stack.Navigator           initialRouteName="Home"           screenOptions={{headerStyle:{backgroundColor:'#8B4513',// 传统木色},headerTintColor:'#FFF',headerTitleStyle:{fontFamily:'Chinese-Calligraphy',fontSize:24,},}}><Stack.Screen              name="Home"              component={HomeScreen}             options={{title:'姓铭堂人类大学'}}/><Stack.Screen              name="Learning"              component={LearningScreen}             options={{title:'文明学习'}}/><Stack.Screen              name="Social"              component={SocialScreen}             options={{title:'全球社交'}}/><Stack.Screen              name="Metaverse"              component={MetaverseScreen}             options={{title:'元宇宙场景'}}/><Stack.Screen              name="GrowthTree"              component={GrowthTreeScreen}             options={{title:'文明成长树'}}/><Stack.Screen              name="Profile"              component={ProfileScreen}             options={{title:'我的文明档案'}}/></Stack.Navigator></NavigationContainer></Provider>);}

3.2 文明成长树组件

javascript

// GrowthTreeVisualization.jsimport React,{ useRef, useEffect, useState }from'react';import{ View, Dimensions, StyleSheet }from'react-native';import Svg,{G, Path, Circle, Text, Defs, LinearGradient, Stop }from'react-native-svg';import*as d3 from'd3-shape';const{ width, height }= Dimensions.get('window');constGrowthTreeVisualization=({ treeData })=>{const svgRef =useRef(null);const[treePaths, setTreePaths]=useState([]);const[fruits, setFruits]=useState([]);useEffect(()=>{if(treeData){renderTree();}},[treeData]);constrenderTree=()=>{// 计算根系路径const rootPaths =calculateRootPaths(treeData.rootSystem);// 计算树干路径const trunkPath =calculateTrunkPath(treeData.trunkSystem);// 计算枝叶路径const branchPaths =calculateBranchPaths(treeData.branches);// 计算果实位置const fruitPositions =calculateFruitPositions(treeData.fruits);setTreePaths([...rootPaths, trunkPath,...branchPaths]);setFruits(fruitPositions);};constcalculateTrunkPath=(trunkSystem)=>{const{        selfCultivationRings,        familyHarmonyRings,        careerDevelopmentRings,        universePeaceRings      }= trunkSystem;const totalRings = selfCultivationRings + familyHarmonyRings +                        careerDevelopmentRings + universePeaceRings;const trunkHeight = Math.min(400, totalRings *20);// 创建树干路径const trunkGenerator = d3.line().curve(d3.curveBasis).x(d=> d.x).y(d=> d.y);const trunkPoints =[{x: width /2,y: height -100},// 根部{x: width /2,y: height -100- trunkHeight *0.3},{x: width /2,y: height -100- trunkHeight *0.6},{x: width /2,y: height -100- trunkHeight },// 顶部];return{type:'trunk',path:trunkGenerator(trunkPoints),strokeWidth:20,strokeColor:'#8B4513',gradient:true};};constcalculateRootPaths=(rootSystem)=>{const{ surnameDepth, cultureDepth, bloodlineDepth }= rootSystem;const paths =[];// 姓氏根const surnamePath =createRootPath(       width /2, height -100,        surnameDepth *5,-Math.PI/6,'#C19A6B');     surnamePath.label =`姓氏渊源 (${surnameDepth}%)`;     paths.push(surnamePath);// 文化根const culturePath =createRootPath(       width /2, height -100,        cultureDepth *5,0,'#D2691E');     culturePath.label =`文化根基 (${cultureDepth}%)`;     paths.push(culturePath);// 血脉根const bloodlinePath =createRootPath(       width /2, height -100,        bloodlineDepth *5, Math.PI/6,'#8B4513');     bloodlinePath.label =`血脉传承 (${bloodlineDepth}%)`;     paths.push(bloodlinePath);return paths;};constcreateRootPath=(startX, startY, length, angle, color)=>{const endX = startX + Math.cos(angle)* length;const endY = startY + Math.sin(angle)* length;const generator = d3.line().curve(d3.curveBasis);const points =[{x: startX,y: startY },{x: startX + Math.cos(angle)* length *0.3,y: startY + Math.sin(angle)* length *0.3},{x: endX,y: endY }];return{type:'root',path:generator(points),strokeWidth:10,strokeColor: color    };};constcalculateBranchPaths=(branches)=>{return branches.map((branch, index)=>{const{ type, growthLevel, connectionCount }= branch;// 计算位置和大小const angle =(index / branches.length)* Math.PI*2;const radius =100+ growthLevel *10;const startX = width /2;const startY = height -300;const endX = startX + Math.cos(angle)* radius;const endY = startY + Math.sin(angle)* radius;const generator = d3.line().curve(d3.curveBasis);const points =[{x: startX,y: startY },{x: startX + Math.cos(angle)* radius *0.5,y: startY + Math.sin(angle)* radius *0.5},{x: endX,y: endY }];// 根据关系类型选择颜色const colors ={'同姓宗亲':'#FF6B6B','互为语伴':'#4ECDC4','同趣好友':'#FFD166','合作伙伴':'#06D6A0','金兰之交':'#118AB2','婚恋一家':'#EF476F','创新发展':'#7209B7','灵魂伴侣':'#F72585'};return{type:'branch',path:generator(points),strokeWidth:5+ connectionCount *0.5,strokeColor: colors[type]||'#000',label:`${type} (${connectionCount})`,         endX,         endY      };});};constcalculateFruitPositions=(fruits)=>{return fruits.map((fruit, index)=>{const angle = Math.random()* Math.PI*2;const distance =150+ Math.random()*100;return{id: fruit.id,x: width /2+ Math.cos(angle)* distance,y: height -350+ Math.sin(angle)* distance,type: fruit.type,rarity: fruit.rarity,energyValue: fruit.energyValue      };});};constgetFruitColor=(type)=>{const colors ={'智慧果':'#FFD700','友谊果':'#FF6B6B','创造果':'#4ECDC4','传承果':'#8B4513','文明果':'#7209B7'};return colors[type]||'#FFD700';};constgetFruitSize=(rarity)=>{const sizes ={'普通':12,'稀有':16,'史诗':20,'传说':24,'文明':28};return sizes[rarity]||12;};return(<View style={styles.container}><Svg width={width} height={height} ref={svgRef}><Defs><LinearGradient id="trunkGradient" x1="0%" y1="0%" x2="0%" y2="100%"><Stop offset="0%" stopColor="#8B4513" stopOpacity={1}/><Stop offset="100%" stopColor="#5D2906" stopOpacity={1}/></LinearGradient></Defs>{/* 绘制树 */}{treePaths.map((item, index)=>(<G key={index}><Path               d={item.path}               stroke={item.strokeColor}               strokeWidth={item.strokeWidth}               fill="none"               strokeLinecap="round"               strokeLinejoin="round"/>{item.label && item.endX &&(<Text                 x={item.endX}                 y={item.endY -20}                 fill={item.strokeColor}                 fontSize="12"                 textAnchor="middle"                 fontFamily="Noto-Sans-SC">{item.label}</Text>)}</G>))}{/* 绘制果实 */}{fruits.map((fruit)=>(<G key={fruit.id}><Circle               cx={fruit.x}               cy={fruit.y}               r={getFruitSize(fruit.rarity)}               fill={getFruitColor(fruit.type)}               stroke="#FFF"               strokeWidth={2}/><Text               x={fruit.x}               y={fruit.+4}               fill="#FFF"               fontSize="10"               textAnchor="middle"               fontWeight="bold">{fruit.energyValue}</Text></G>))}{/* 能量值显示 */}<Circle           cx={width /2}           cy={height -50}           r={30}           fill="#FFD700"           stroke="#FFA500"           strokeWidth={3}/><Text           x={width /2}           y={height -45}           fill="#000"           fontSize="16"           textAnchor="middle"           fontWeight="bold">{treeData?.totalEnergy ||0}</Text><Text           x={width /2}           y={height -25}           fill="#666"           fontSize="12"           textAnchor="middle">           文明能量        </Text></Svg></View>);};const styles = StyleSheet.create({container:{flex:1,backgroundColor:'#F5F5DC',// 羊皮纸色},});exportdefault GrowthTreeVisualization;

3.3 AI语伴聊天界面

javascript

// AIChatScreen.jsimport React,{ useState, useEffect, useRef }from'react';import{   View,   Text,   TextInput,   FlatList,   TouchableOpacity,   StyleSheet,   KeyboardAvoidingView,   Platform,   ActivityIndicator,}from'react-native';import{ Ionicons }from'@expo/vector-icons';import Voice from'@react-native-voice/voice';import Tts from'react-native-tts';constAIChatScreen=({ route })=>{const{ companionId, companionName, languageLevel }= route.params;const[messages, setMessages]=useState([]);const[inputText, setInputText]=useState('');const[isListening, setIsListening]=useState(false);const[isAIThinking, setIsAIThinking]=useState(false);const[voiceEnabled, setVoiceEnabled]=useState(true);const flatListRef =useRef(null);// 初始化语音useEffect(()=>{     Tts.setDefaultLanguage('zh-CN');     Tts.setDefaultRate(0.5);     Tts.setDefaultPitch(1.0);          Voice.onSpeechResults = onSpeechResults;     Voice.onSpeechError = onSpeechError;// 初始问候sendGreeting();return()=>{       Voice.destroy().then(Voice.removeAllListeners);       Tts.stop();};},[]);constsendGreeting=async()=>{const greeting = languageLevel ==='beginner'?`你好!我是你的中文语伴${companionName}。我们可以练习日常对话。`:`欢迎回来!今天想聊什么话题呢?`;addMessage('ai', greeting);if(voiceEnabled){       Tts.speak(greeting);}};const addMessage =(sender, text, type ='text', data ={})=>{const newMessage ={id: Date.now().toString(),       sender,       text,       type,       data,timestamp:newDate(),};setMessages(prev=>[...prev, newMessage]);// 滚动到底部setTimeout(()=>{       flatListRef.current?.scrollToEnd({animated:true});},100);};consthandleSend=async()=>{if(!inputText.trim())return;// 添加用户消息addMessage('user', inputText);// 清空输入const userText = inputText;setInputText('');// AI思考setIsAIThinking(true);try{// 调用AI服务const response =awaitfetchAIResponse(userText, languageLevel);// 添加AI回复addMessage('ai', response.text, response.type, response.data);// 语音播放if(voiceEnabled && response.text){         Tts.speak(response.text);}// 如果有学习建议,稍后显示if(response.learningTips){setTimeout(()=>{addMessage('ai',`💡 学习建议:${response.learningTips}`,'tip');},1000);}}catch(error){       console.error('AI响应错误:', error);addMessage('ai','抱歉,我暂时无法理解。请再说一遍?');}finally{setIsAIThinking(false);}};constfetchAIResponse=async(userInput, level)=>{const response =awaitfetch(`${API_BASE}/ai/companion/response`,{method:'POST',headers:{'Content-Type':'application/json','Authorization':`Bearer ${userToken}`,},body:JSON.stringify({         userInput,         companionId,languageLevel: level,context:{previousMessages: messages.slice(-5).map(m=>({sender: m.sender,text: m.text,})),},}),});if(!response.ok){thrownewError('网络请求失败');}return response.json();};conststartListening=async()=>{try{setIsListening(true);await Voice.start('zh-CN');}catch(e){       console.error('语音识别启动失败:', e);setIsListening(false);}};conststopListening=async()=>{try{await Voice.stop();setIsListening(false);}catch(e){       console.error('语音识别停止失败:', e);}};constonSpeechResults=(e)=>{setInputText(e.value[0]);stopListening();};constonSpeechError=(e)=>{     console.error('语音识别错误:', e);setIsListening(false);};consttoggleVoice=()=>{setVoiceEnabled(!voiceEnabled);if(!voiceEnabled){       Tts.stop();}};constrenderMessage=({ item })=>{const isUser = item.sender ==='user';return(<View style={[         styles.messageContainer,         isUser ? styles.userMessage : styles.aiMessage,]}>{!isUser &&(<View style={styles.avatar}><Text style={styles.avatarText}>{companionName.charAt(0)}</Text></View>)}<View style={[           styles.messageBubble,           isUser ? styles.userBubble : styles.aiBubble,]}><Text style={[             styles.messageText,             isUser ? styles.userText : styles.aiText,]}>{item.text}</Text>{item.type ==='correction'&&(<View style={styles.correctionBox}><Text style={styles.correctionLabel}>建议修改:</Text><Text style={styles.correctedText}>{item.data.corrected}</Text><Text style={styles.grammarExplanation}>{item.data.explanation}</Text></View>)}{item.type ==='cultural'&&(<View style={styles.culturalBox}><Text style={styles.culturalLabel}>文化解释:</Text><Text style={styles.culturalText}>{item.data.explanation}</Text></View>)}{item.type ==='tip'&&(<View style={styles.tipBox}><Ionicons name="bulb" size={16} color="#FFD700"/><Text style={styles.tipText}>{item.text}</Text></View>)}</View>{isUser &&(<View style={[styles.avatar, styles.userAvatar]}><Text style={styles.avatarText}>{userInitial}</Text></View>)}</View>);};return(<KeyboardAvoidingView       style={styles.container}       behavior={Platform.OS==='ios'?'padding':'height'}>{/* 聊天区域 */}<FlatList         ref={flatListRef}         data={messages}         renderItem={renderMessage}         keyExtractor={item=> item.id}         style={styles.messageList}         contentContainerStyle={styles.messageListContent}/>{/* 输入区域 */}<View style={styles.inputContainer}><TouchableOpacity           style={[styles.voiceButton, isListening && styles.listeningButton]}           onPress={isListening ? stopListening : startListening}><Ionicons              name={isListening ?"stop-circle":"mic"}              size={24}              color={isListening ?"#FF4444":"#666"}/></TouchableOpacity><TextInput           style={styles.textInput}           value={inputText}           onChangeText={setInputText}           placeholder="输入中文消息..."           placeholderTextColor="#999"           multiline           maxLength={500}/><TouchableOpacity           style={styles.voiceToggle}           onPress={toggleVoice}><Ionicons              name={voiceEnabled ?"volume-high":"volume-mute"}              size={20}              color={voiceEnabled ?"#4CAF50":"#999"}/></TouchableOpacity><TouchableOpacity           style={[             styles.sendButton,(!inputText.trim()|| isAIThinking)&& styles.sendButtonDisabled,]}           onPress={handleSend}           disabled={!inputText.trim()|| isAIThinking}>{isAIThinking ?(<ActivityIndicator size="small" color="#FFF"/>):(<Ionicons name="send" size={20} color="#FFF"/>)}</TouchableOpacity></View>{/* 快捷短语 */}<View style={styles.quickPhrases}><Text style={styles.quickPhrasesTitle}>快捷短语:</Text><ScrollView horizontal showsHorizontalScrollIndicator={false}>{QUICK_PHRASES[languageLevel].map((phrase, index)=>(<TouchableOpacity               key={index}               style={styles.phraseButton}               onPress={()=>setInputText(phrase)}><Text style={styles.phraseText}>{phrase}</Text></TouchableOpacity>))}</ScrollView></View></KeyboardAvoidingView>);};const styles = StyleSheet.create({container:{flex:1,backgroundColor:'#F5F5F5',},messageList:{flex:1,},messageListContent:{padding:16,},messageContainer:{flexDirection:'row',marginBottom:16,alignItems:'flex-end',},userMessage:{justifyContent:'flex-end',},aiMessage:{justifyContent:'flex-start',},avatar:{width:36,height:36,borderRadius:18,backgroundColor:'#8B4513',justifyContent:'center',alignItems:'center',marginHorizontal:8,},userAvatar:{backgroundColor:'#4A90E2',},avatarText:{color:'#FFF',fontSize:16,fontWeight:'bold',},messageBubble:{maxWidth:'70%',padding:12,borderRadius:18,},userBubble:{backgroundColor:'#4A90E2',borderBottomRightRadius:4,},aiBubble:{backgroundColor:'#FFF',borderBottomLeftRadius:4,elevation:2,shadowColor:'#000',shadowOffset:{width:0,height:1},shadowOpacity:0.1,shadowRadius:2,},messageText:{fontSize:16,lineHeight:22,},userText:{color:'#FFF',},aiText:{color:'#333',},correctionBox:{marginTop:8,padding:8,backgroundColor:'#FFF8E1',borderRadius:8,borderLeftWidth:3,borderLeftColor:'#FFB300',},correctionLabel:{fontSize:12,color:'#FF8F00',fontWeight:'bold',marginBottom:4,},correctedText:{fontSize:14,color:'#333',fontWeight:'500',},grammarExplanation:{fontSize:12,color:'#666',marginTop:4,fontStyle:'italic',},culturalBox:{marginTop:8,padding:8,backgroundColor:'#E8F5E9',borderRadius:8,borderLeftWidth:3,borderLeftColor:'#4CAF50',},culturalLabel:{fontSize:12,color:'#2E7D32',fontWeight:'bold',marginBottom:4,},culturalText:{fontSize:14,color:'#333',},tipBox:{marginTop:8,padding:8,backgroundColor:'#FFFDE7',borderRadius:8,flexDirection:'row',alignItems:'center',},tipText:{fontSize:14,color:'#333',marginLeft:8,flex:1,},inputContainer:{flexDirection:'row',alignItems:'center',padding:12,backgroundColor:'#FFF',borderTopWidth:1,borderTopColor:'#E0E0E0',},voiceButton:{padding:8,marginRight:8,},listeningButton:{backgroundColor:'#FFEBEE',borderRadius:20,},textInput:{flex:1,minHeight:40,maxHeight:120,paddingHorizontal:12,paddingVertical:8,backgroundColor:'#F5F5F5',borderRadius:20,fontSize:16,color:'#333',},voiceToggle:{padding:8,marginHorizontal:8,},sendButton:{width:40,height:40,borderRadius:20,backgroundColor:'#8B4513',justifyContent:'center',alignItems:'center',},sendButtonDisabled:{backgroundColor:'#CCCCCC',},quickPhrases:{padding:12,backgroundColor:'#FFF',borderTopWidth:1,borderTopColor:'#E0E0E0',},quickPhrasesTitle:{fontSize:14,color:'#666',marginBottom:8,fontWeight:'500',},phraseButton:{paddingHorizontal:12,paddingVertical:6,backgroundColor:'#F5F5F5',borderRadius:16,marginRight:8,},phraseText:{fontSize:14,color:'#333',},});constQUICK_PHRASES={beginner:["你好吗?","今天天气怎么样?","我喜欢吃中国菜","你叫什么名字?","谢谢你的帮助",],intermediate:["我们可以练习日常对话吗?","这个词怎么发音?","中国文化很有趣","你的兴趣爱好是什么?","周末你打算做什么?",],advanced:["你对人工智能有什么看法?","中华文明有哪些特点?","跨文化交流的重要性","如何学习中文更有效?","未来科技发展趋势",],};exportdefault AIChatScreen;

3.4 元宇宙场景组件(Three.js)

javascript

// MetaverseScene.jsimport React,{ useRef, useEffect, useState }from'react';import{ Canvas, useThree, useFrame }from'@react-three/fiber';import{    OrbitControls,    Sky,    Stars,    Text,    Html  }from'@react-three/drei';import*asTHREEfrom'three';import{ VRButton,XR, Controllers, Hands }from'@react-three/xr';// 场景组件constAncestralTempleScene=({ userId })=>{const[sceneReady, setSceneReady]=useState(false);const[userAvatar, setUserAvatar]=useState(null);const[otherAvatars, setOtherAvatars]=useState([]);const[interactableObjects, setInteractableObjects]=useState([]);useEffect(()=>{loadScene();loadUserAvatar();loadOtherAvatars();return()=>{// 清理资源};},[]);constloadScene=async()=>{// 加载宗祠模型const templeModel =awaitloadGLTFModel('/models/temple.glb');// 加载牌位模型const memorialTablets =awaitloadGLTFModel('/models/memorial_tablets.glb');// 加载香炉模型const incenseBurner =awaitloadGLTFModel('/models/incense_burner.glb');// 设置交互setupInteractions([{object: incenseBurner,type:'RITUAL',action:'burn_incense'},{object: memorialTablets,type:'VIEW',action:'view_genealogy'},]);setSceneReady(true);};consthandleObjectInteraction=(object, interactionType)=>{switch(interactionType){case'burn_incense':performIncenseRitual();break;case'view_genealogy':showGenealogyViewer();break;default:         console.log('未知交互类型');}};constperformIncenseRitual=()=>{// 焚香仪式动画// 播放动画// 发送完成事件// 发放奖励};return(<><VRButton /><Canvas         camera={{position:[0,1.6,5],fov:75}}         shadows         style={{width:'100%',height:'100%'}}><XR><Controllers /><Hands />{/* 环境 */}<ambientLight intensity={0.6}/><directionalLight             position={[10,10,5]}             intensity={1}             castShadow             shadow-mapSize-width={2048}             shadow-mapSize-height={2048}/><Sky sunPosition={[100,20,100]}/>{/* 地面 */}<mesh rotation={[-Math.PI/2,0,0]} receiveShadow><planeGeometry args={[100,100]}/><shadowMaterial opacity={0.3}/><meshStandardMaterial color="#8B4513"/></mesh>{/* 宗祠建筑 */}{sceneReady &&(<><TempleBuilding position={[0,0,0]}/><MemorialTablets position={[0,1,-3]}/><IncenseBurner                  position={[0,0.5,-2]}                 onClick={()=>handleObjectInteraction('incense_burner','burn_incense')}/>{/* 用户虚拟形象 */}{userAvatar &&(<UserAvatar                    position={[0,0,0]}                   avatarData={userAvatar}/>)}{/* 其他用户虚拟形象 */}{otherAvatars.map((avatar, index)=>(<OtherUserAvatar                   key={avatar.id}                   position={avatar.position}                   avatarData={avatar}/>))}</>)}<OrbitControls              enablePan={true}             enableZoom={true}             enableRotate={true}             maxPolarAngle={Math.PI/2}/></XR></Canvas>{/* UI叠加层 */}{sceneReady &&(<div className="metaverse-ui"><div className="scene-controls"><button onClick={()=> window.history.back()}>               返回            </button><button onClick={openSceneMenu}>               场景菜单            </button></div><div className="user-list"><h4>在线用户</h4>{/* 显示在线用户列表 */}</div><div className="interaction-hints"><p>点击香炉进行焚香仪式</p><p>点击牌位查看家谱</p></div></div>)}</>);};// 3D模型组件constTempleBuilding=({ position })=>{const templeRef =useRef();useFrame((state)=>{// 添加轻微动画if(templeRef.current){       templeRef.current.rotation.= Math.sin(state.clock.elapsedTime *0.1)*0.01;}});return(<group ref={templeRef} position={position}>{/* 建筑主体 */}<mesh castShadow receiveShadow><boxGeometry args={[8,6,10]}/><meshStandardMaterial color="#8B4513"/></mesh>{/* 屋顶 */}<mesh position={[0,3.5,0]} castShadow><coneGeometry args={[5,2,4]}/><meshStandardMaterial color="#5D2906"/></mesh>{/* 柱子 */}{[-3,-1,1,3].map((x, i)=>(<mesh key={i} position={[x,3,-4]} castShadow><cylinderGeometry args={[0.3,0.3,6]}/><meshStandardMaterial color="#C19A6B"/></mesh>))}{/* 牌匾 */}<mesh position={[0,4,5]} rotation={[0,0,0]}><planeGeometry args={[4,0.8]}/><meshBasicMaterial color="#000"><Html center><div style={{color:'gold',fontSize:'24px',fontFamily:'Chinese-Calligraphy',textAlign:'center',width:'100%',}}>               姓铭堂            </div></Html></meshBasicMaterial></mesh></group>);};constIncenseBurner=({ position, onClick })=>{const burnerRef =useRef();const[isBurning, setIsBurning]=useState(false);const smokeParticles =useRef([]);consthandleClick=()=>{if(!isBurning){setIsBurning(true);onClick();// 10秒后停止setTimeout(()=>setIsBurning(false),10000);}};useFrame((state)=>{if(isBurning && burnerRef.current){// 生成烟雾粒子if(Math.random()>0.7){const particle ={position:newTHREE.Vector3(             position[0]+(Math.random()-0.5)*0.2,             position[1]+1,             position[2]+(Math.random()-0.5)*0.2),velocity:newTHREE.Vector3((Math.random()-0.5)*0.01,0.02+ Math.random()*0.01,(Math.random()-0.5)*0.01),life:1.0,size:0.1+ Math.random()*0.2};                  smokeParticles.current.push(particle);}// 更新粒子       smokeParticles.current = smokeParticles.current        .map(p=>({...p,position: p.position.add(p.velocity),life: p.life -0.01})).filter(p=> p.life >0);}});return(<group ref={burnerRef} position={position}>{/* 香炉主体 */}<mesh onClick={handleClick} castShadow><cylinderGeometry args={[0.5,0.7,0.8,16]}/><meshStandardMaterial            color={isBurning ?"#FFA500":"#8B4513"}            emissive={isBurning ?"#FFA500":"#000000"}           emissiveIntensity={isBurning ?0.5:0}/></mesh>{/* 香 */}<mesh position={[0,0.5,0]} visible={isBurning}><cylinderGeometry args={[0.02,0.02,1,8]}/><meshStandardMaterial color="#8B0000"/></mesh>{/* 烟雾粒子 */}{isBurning && smokeParticles.current.map((particle, i)=>(<mesh key={i} position={particle.position.toArray()}><sphereGeometry args={[particle.size * particle.life,8,8]}/><meshBasicMaterial              color="#FFFFFF"              transparent              opacity={particle.life *0.3}/></mesh>))}{/* 交互提示 */}<Html position={[0,1.5,0]}><div style={{background:'rgba(0,0,0,0.7)',color:'white',padding:'8px 12px',borderRadius:'8px',fontSize:'14px',whiteSpace:'nowrap',cursor:'pointer',}} onClick={handleClick}>{isBurning ?'焚香中...':'点击焚香'}</div></Html></group>);};exportdefault AncestralTempleScene;

四、部署与运维配置

4.1 Docker 配置

dockerfile

# Dockerfile - 用户服务示例 FROM openjdk:17-jdk-slim  # 设置工作目录 WORKDIR /app  # 复制依赖文件 COPY backend/surname-user-service/pom.xml . COPY backend/surname-user-service/mvnw . COPY backend/surname-user-service/.mvn .mvn  # 下载依赖 RUN ./mvnw dependency:go-offline  # 复制源代码 COPY backend/surname-user-service/src ./src  # 构建应用 RUN ./mvnw clean package -DskipTests  # 运行应用 EXPOSE 8080 ENTRYPOINT ["java", "-jar", "target/user-service-1.0.0.jar"]

dockerfile

# Dockerfile - 前端应用 FROM node:18-alpine as build  # 设置工作目录 WORKDIR /app  # 复制依赖文件 COPY frontend/web/package*.json ./  # 安装依赖 RUN npm ci --only=production  # 复制源代码 COPY frontend/web/ .  # 构建应用 RUN npm run build  # 生产环境 FROM nginx:alpine  # 复制构建文件 COPY --from=build /app/build /usr/share/nginx/html  # 复制nginx配置 COPY frontend/web/nginx.conf /etc/nginx/conf.d/default.conf  EXPOSE 80  CMD ["nginx", "-g", "daemon off;"]

4.2 Kubernetes 部署配置

yaml

# kubernetes/user-service-deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata:name: surname-user-service  namespace: surname-universityspec:replicas:3selector:matchLabels:app: surname-user-service  template:metadata:labels:app: surname-user-service    spec:containers:-name: user-service        image: registry.surname.com/user-service:1.0.0        ports:-containerPort:8080env:-name: SPRING_PROFILES_ACTIVE          value:"prod"-name: DB_HOST          valueFrom:configMapKeyRef:name: app-config              key: db.host        -name: REDIS_HOST          valueFrom:configMapKeyRef:name: app-config              key: redis.host        resources:requests:memory:"512Mi"cpu:"250m"limits:memory:"1Gi"cpu:"500m"livenessProbe:httpGet:path: /actuator/health            port:8080initialDelaySeconds:60periodSeconds:10readinessProbe:httpGet:path: /actuator/health/readiness            port:8080initialDelaySeconds:30periodSeconds:5---apiVersion: v1kind: Servicemetadata:name: surname-user-service  namespace: surname-universityspec:selector:app: surname-user-service  ports:-port:80targetPort:8080type: ClusterIP

4.3 CI/CD 流水线配置

yaml

# .gitlab-ci.ymlstages:- test  - build  - deployvariables:DOCKER_REGISTRY: registry.surname.com  KUBE_NAMESPACE: surname-university# 后端服务测试backend-test:stage: test  image: maven:3.8-openjdk-17script:- cd backend/surname-user-service    - mvn clean test  only:- merge_requests    - main# 前端测试frontend-test:stage: test  image: node:18-alpine  script:- cd frontend/web    - npm ci    - npm test  only:- merge_requests    - main# 构建Docker镜像build-backend:stage: build  image: docker:20.10services:- docker:20.10-dind  script:- docker build -t $DOCKER_REGISTRY/user-service:$CI_COMMIT_SHA -f backend/surname-user-service/Dockerfile .    - docker push $DOCKER_REGISTRY/user-service:$CI_COMMIT_SHA  only:- main# 部署到Kubernetesdeploy-prod:stage: deploy  image: bitnami/kubectl:latest  script:- kubectl set image deployment/surname-user-service user-service=$DOCKER_REGISTRY/user-service:$CI_COMMIT_SHA -n $KUBE_NAMESPACE    - kubectl rollout status deployment/surname-user-service -n $KUBE_NAMESPACE --timeout=300s  only:- main  environment:name: production    url: https://app.surname-university.com

五、监控与日志配置

5.1 Prometheus 监控配置

yaml

# prometheus/prometheus.ymlglobal:scrape_interval: 15s  evaluation_interval: 15salerting:alertmanagers:-static_configs:-targets:['alertmanager:9093']rule_files:-"alerts.yml"scrape_configs:-job_name:'spring-boot-apps'metrics_path:'/actuator/prometheus'static_configs:-targets:-'surname-user-service:8080'-'surname-learning-service:8080'-'surname-social-service:8080'labels:application:'surname-university'environment:'production'-job_name:'node-exporter'static_configs:-targets:['node-exporter:9100']-job_name:'redis-exporter'static_configs:-targets:['redis-exporter:9121']

5.2 Grafana 仪表板配置

json

{"dashboard":{"title":"姓铭堂人类大学监控","panels":[{"title":"用户增长趋势","targets":[{"expr":"sum(increase(user_registrations_total[1d]))","legendFormat":"{{instance}}"}],"type":"graph"},{"title":"文明能量分布","targets":[{"expr":"histogram_quantile(0.95, rate(civilization_energy_bucket[5m]))","legendFormat":"95分位数"}],"type":"heatmap"},{"title":"API响应时间","targets":[{"expr":"histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))","legendFormat":"P99响应时间"}],"type":"stat"}]}}

六、安全配置

6.1 Spring Security 配置

java

// SecurityConfig.java@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled =true)publicclassSecurityConfig{@AutowiredprivateJwtAuthenticationFilter jwtAuthenticationFilter;@AutowiredprivateUserDetailsService userDetailsService;@BeanpublicSecurityFilterChainfilterChain(HttpSecurity http)throwsException{         http            .cors(Customizer.withDefaults()).csrf(AbstractHttpConfigurer::disable).sessionManagement(session ->                  session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).authorizeHttpRequests(auth -> auth                .requestMatchers("/api/auth/**").permitAll().requestMatchers("/api/public/**").permitAll().requestMatchers("/actuator/health").permitAll().requestMatchers("/api/admin/**").hasRole("ADMIN").requestMatchers("/api/**").authenticated().anyRequest().authenticated()).addFilterBefore(jwtAuthenticationFilter,UsernamePasswordAuthenticationFilter.class).exceptionHandling(exceptions -> exceptions                .authenticationEntryPoint(newJwtAuthenticationEntryPoint()).accessDeniedHandler(newJwtAccessDeniedHandler()));return http.build();}@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}@BeanpublicAuthenticationManagerauthenticationManager(AuthenticationConfiguration authConfig)throwsException{return authConfig.getAuthenticationManager();}}

6.2 JWT 认证服务

java

// JwtService.java@Service@Slf4jpublicclassJwtService{@Value("${jwt.secret}")privateString secretKey;@Value("${jwt.expiration}")privateLong expiration;publicStringgenerateToken(UserDetails userDetails){Map<String,Object> claims =newHashMap<>();         claims.put("userId",((CustomUserDetails) userDetails).getUserId());         claims.put("roles", userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));returnJwts.builder().setClaims(claims).setSubject(userDetails.getUsername()).setIssuedAt(newDate()).setExpiration(newDate(System.currentTimeMillis()+ expiration)).signWith(SignatureAlgorithm.HS256, secretKey).compact();}publicbooleanvalidateToken(String token){try{Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);returntrue;}catch(JwtException|IllegalArgumentException e){             log.error("JWT令牌验证失败: {}", e.getMessage());returnfalse;}}publicAuthenticationgetAuthentication(String token){Claims claims =extractClaims(token);String username = claims.getSubject();List<String> roles = claims.get("roles",List.class);Collection<SimpleGrantedAuthority> authorities = roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());returnnewUsernamePasswordAuthenticationToken(             username,null, authorities);}privateClaimsextractClaims(String token){returnJwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();}}

七、测试代码

7.1 单元测试示例

java

// UserServiceTest.java@ExtendWith(MockitoExtension.class)classUserServiceTest{@MockprivateUserRepository userRepository;@MockprivateGrowthTreeService growthTreeService;@MockprivateCivilizationService civilizationService;@InjectMocksprivateUserService userService;@TestvoidtestRegisterUser_Success(){// 准备测试数据UserRegisterDTO dto =newUserRegisterDTO();         dto.setUsername("测试用户");         dto.setEmail("test@example.com");         dto.setPassword("password123");         dto.setSurname("张");// 模拟依赖行为when(userRepository.existsByEmail(anyString())).thenReturn(false);when(userRepository.save(any(User.class))).thenAnswer(invocation ->{User user = invocation.getArgument(0);             user.setId(1L);return user;});// 执行测试UserVO result = userService.register(dto);// 验证结果assertNotNull(result);assertEquals("张", result.getSurname());assertEquals("test@example.com", result.getEmail());// 验证依赖调用verify(userRepository).existsByEmail("test@example.com");verify(userRepository).save(any(User.class));verify(growthTreeService).createGrowthTree(1L);verify(civilizationService).initLearningTasks(1L);}@TestvoidtestRegisterUser_EmailExists(){// 准备测试数据UserRegisterDTO dto =newUserRegisterDTO();         dto.setEmail("existing@example.com");// 模拟邮箱已存在when(userRepository.existsByEmail("existing@example.com")).thenReturn(true);// 执行测试并验证异常BusinessException exception =assertThrows(BusinessException.class,()-> userService.register(dto));assertEquals(ErrorCode.USER_EMAIL_EXIST, exception.getCode());}}// IntegrationTest.java@SpringBootTest@AutoConfigureMockMvcclassUserIntegrationTest{@AutowiredprivateMockMvc mockMvc;@AutowiredprivateObjectMapper objectMapper;@TestvoidtestUserRegistrationFlow()throwsException{// 1. 注册用户UserRegisterDTO registerDTO =newUserRegisterDTO();         registerDTO.setUsername("集成测试用户");         registerDTO.setEmail("integration@test.com");         registerDTO.setPassword("Test@123");         registerDTO.setSurname("李");MvcResult registerResult = mockMvc.perform(post("/api/v1/users/register").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(registerDTO))).andExpect(status().isOk()).andReturn();// 2. 登录获取tokenLoginDTO loginDTO =newLoginDTO();         loginDTO.setEmail("integration@test.com");         loginDTO.setPassword("Test@123");MvcResult loginResult = mockMvc.perform(post("/api/v1/auth/login").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(loginDTO))).andExpect(status().isOk()).andReturn();String token =extractToken(loginResult);// 3. 使用token访问受保护接口         mockMvc.perform(get("/api/v1/users/profile").header("Authorization","Bearer "+ token)).andExpect(status().isOk()).andExpect(jsonPath("$.data.username").value("集成测试用户"));}}

八、数据库迁移脚本

8.1 Flyway 迁移脚本

sql

-- V1__initial_schema.sqlCREATETABLE users (     id BIGINTAUTO_INCREMENTPRIMARYKEY,     uid VARCHAR(36)NOTNULLUNIQUE,     username VARCHAR(50)NOTNULL,     email VARCHAR(100)NOTNULLUNIQUE,     phone VARCHAR(20),     surname VARCHAR(50),     surname_origin TEXT,     surname_story TEXT,     learning_index INTDEFAULT0,     social_index INTDEFAULT0,     creation_index INTDEFAULT0,     heritage_index INTDEFAULT0,     civilization_energy INTDEFAULT0,     create_time TIMESTAMPDEFAULTCURRENT_TIMESTAMP,     update_time TIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,INDEX idx_email (email),INDEX idx_surname (surname));CREATETABLE growth_trees (     id BIGINTAUTO_INCREMENTPRIMARYKEY,     user_id BIGINTNOTNULLUNIQUE,     surname_depth INTDEFAULT10,     culture_depth INTDEFAULT5,     bloodline_depth INTDEFAULT0,     self_cultivation_rings INTDEFAULT1,     family_harmony_rings INTDEFAULT0,     career_development_rings INTDEFAULT0,     universe_peace_rings INTDEFAULT0,     total_energy INTDEFAULT0,     last_update_time TIMESTAMPDEFAULTCURRENT_TIMESTAMP,FOREIGNKEY(user_id)REFERENCES users(id)ONDELETECASCADE);CREATETABLE relationships (     id BIGINTAUTO_INCREMENTPRIMARYKEY,     user_id BIGINTNOTNULL,     target_user_id BIGINTNOTNULL,     relationship_type ENUM('SAME_SURNAME','LANGUAGE_PARTNER','INTEREST_FRIEND','BUSINESS_PARTNER','GOLDEN_ORCHID','MARRIAGE','INNOVATION','SOUL_MATE')NOTNULL,levelINTDEFAULT1,     intensity DECIMAL(5,2)DEFAULT0.0,     last_interaction TIMESTAMPDEFAULTCURRENT_TIMESTAMP,     nft_token_id VARCHAR(100),     create_time TIMESTAMPDEFAULTCURRENT_TIMESTAMP,UNIQUEKEY uk_user_relationship (user_id, target_user_id, relationship_type),FOREIGNKEY(user_id)REFERENCES users(id)ONDELETECASCADE,FOREIGNKEY(target_user_id)REFERENCES users(id)ONDELETECASCADE,INDEX idx_relationship_type (relationship_type));-- V2__add_civilization_steps.sqlALTERTABLE users  ADDCOLUMN civilization_steps JSON;UPDATE users  SET civilization_steps = JSON_OBJECT('图腾崇拜',10,'部落标识',0,'象形造字',0,'姓氏传承',5,'家国情怀',0,'修齐治平',0,'香火不绝',0,'文明永续',0,'和美永生',0);

总结

这是一个完整的《姓铭堂人类大学APP》技术架构和核心代码实现方案。由于项目规模庞大,这里提供的是架构设计和核心代码示例,实际开发需要:

  1. 组建专业团队:包括后端、前端、AI、区块链、3D开发等专业人才

  2. 分阶段开发:建议采用敏捷开发,每3个月一个重大版本

  3. 云原生部署:采用微服务架构,确保高可用和可扩展性

  4. 持续测试:建立完整的自动化测试体系

  5. 安全合规:特别关注数据隐私和跨境合规问题

  6. 文化敏感性:确保多语言多文化的正确呈现

预计开发周期

  • MVP版本:3-4个月

  • 核心功能完整版:8-10个月

  • 平台成熟版:1.5-2年

预计团队规模

  • 初期:15-20人(全栈工程师为主)

  • 成长期:30-50人(增加专业领域工程师)

  • 成熟期:80-100人(包括运营、内容、市场团队)

此架构设计支持从百万级到亿级用户的扩展,满足《大长征·人类铭》理念的完整实现需求。

已关注

关注

重播 分享

央视专访姓铭堂创始人王光杰先生

——大长征•人类铭作者

《大长征 · 宇宙人类学》主编,

大长征•人类铭作者简介

王光杰——

中华姓氏非遗大工匠,

中央电视台财富新榜样,

央视专访链接:

https://mp.weixin.qq.com/s/BOyboPxxkEgEhgzejQVHuw

深圳市姓铭堂文化中心创始人

世享华文(深圳)智能公司董事长

姓铭堂人类大学

总策划、发起人,

百佳诗人,优秀作家,

工艺美术师,南粤改革先锋,

微信公众号“大长征GLM”主笔,

代表作——

《大长征•人类铭》

《大长征•宇宙人类学》、

《大长征•中国时代》、

《大长征•中华姓氏学》

《大长征•自主健康学》等,

香港-台北-上海-深圳四城文化交流年会主讲嘉宾,

北京大学中美总栽博士班客座教授,

北京中华文化促进会华祖文化使者,

国家拜祖大典贵宾(河南新郑)

国家公祭黄帝大典贵宾(陕西黄陵)

中国共产党优秀党员,

中华职业教育社社员,

深圳十大杰出青年候选人,

青春中华·中国青年文化周-中华民族特色精品展示会特邀青年艺术家,

中国青少年读书周特邀老师,

深圳市文化创意产业协会副会长,

深圳市工艺美术行业协会副会长,

深圳风云会副主席,

深圳市福顺公益基金会党支部书记,

湾区元宇宙创始委员

荣获:

中华姓氏非遗大工匠奖,

中国工艺美术百花奖金奖,

深圳旅游纪念品设计大赛金奖,

深圳版权金奖(作品奖),

北京文博会最佳展示奖,

杭州西湖博览会国际艺术精品暨中国工艺美术大师作品优秀奖,

丝路精神传承人/千人走戈壁风采领袖奖等

经国家文物局批准

王光杰先生主创的作品“中华姓氏故乡国学系列文创精品”被北京中华民族艺术珍品博物馆永久收藏;

另,美国硅谷亚洲文化中心、北京炎黄艺术馆、北京大观园管理委员会/北京红楼文化艺术博物馆、北戴河中央别墅、深圳图书馆等众多场馆、酒店、会所和超四十万家庭、企业收藏了王光杰先生主创的文化艺术精品。

相关链接请点击
姓铭堂与北大共成长
姓铭堂金奖资质荣誉
请祖回家,感恩励志——姓铭堂姓氏文创精品
寄托乡愁,感恩回馈——姓铭堂故乡文创精品
从姓识己,把握未来——姓铭堂文创经典定制
融合创新,引领时代——姓铭堂文创服务全人类
《姓铭堂人类大学APP》设计方案及对于人类未来领袖人才培养的意义
《人类领导学》提纲、导论
解构《大长征·人类铭》与“人类命运共同体”的哲学-实践统一性——迈向“宇宙尺度命运共同体”的文明演进论
改造人类的学习
宇宙文明教育宪章
大长征·人类宇宙家园宪章
人类文明的结晶与未来发展底座 ——《大长征・人类铭》学术品鉴
《大长征·人类铭·24·志趣》解析 The Analysis of《The Grand Long March • Human Inscription-24· Aspiration-Interest》
《大长征·宇宙人类学》第十七至十八章
《大长征·中国时代》第五卷

《姓铭堂人类大学APP》商业计划书

诚邀任正非先生共建姓铭堂人类大学

诚邀梁文锋先生共建姓铭堂人类大学

诚邀马云先生共建姓铭堂人类大学

诚邀马化腾先生共建姓铭堂人类大学

诚邀华侨城集团共建姓铭堂人类大学

邀请华大基因共建姓铭堂人类大学

诚邀深投控共建姓铭堂人类大学

诚邀北京大学共建姓铭堂人类大学

诚邀深圳大学共建姓铭堂人类大学

诚邀南方科技大学共建姓铭堂人类大学

诚邀深圳广电集团共建姓铭堂人类大学

诚邀华润集团共建姓铭堂人类大学

姓铭堂人类大学的思想哲学基础《大长征•人类经》品鉴

中华示范,人类一家——
中华民族多元一体是人类命运共同体的先行示范
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 《姓铭堂人类大学APP》全套代码架构(下)

评论 抢沙发

9 + 7 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮