# 后端初始化 ## 初始化 springweb lombok mysqldriver 把apprication.properited 改成yml格式 然后加入 ```yml spring: application: name: zds-ai 项目名字 server: port: 2778 servlet: context-path: /api 后端访问的路径吧 ``` ### 依赖整合 hutool knife4j ```xml cn.hutool hutool-all 5.8.38 --> com.github.xiaoymin knife4j-openapi3-jakarta-spring-boot-starter 4.4.0 ``` 改xml配置 ```yml # springdoc-openapi springdoc: group-configs: - group: 'default' packages-to-scan: com.zds.zdsai.controller # knife4j knife4j: enable: true setting: language: zh_cn ``` ### 写模板代码 自定义异常 已经在包中 用就可以了 ### 全局跨域配置(样板代码) 直接在config包中 样板代码 ```java @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { // 覆盖所有请求 registry.addMapping("/**") // 允许发送 Cookie .allowCredentials(true) // 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突) .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .exposedHeaders("*"); } } ``` # 前端初始化 emm 使用nvm 来管理node.js 安装ant design ```shell npm i --save ant-design-vue@4.x ``` 全局装配 ```typescript import './assets/main.css' import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' import router from './router' import Antd from 'ant-design-vue' import 'ant-design-vue/dist/reset.css' const app = createApp(App) app.use(createPinia()) app.use(router) app.use(Antd) app.mount('#app') ``` 安装 axios ``` npm install axios ``` ```ts import axios from 'axios' import { message } from 'ant-design-vue' // 创建 Axios 实例 const myAxios = axios.create({ baseURL: 'http://localhost:8123/api', timeout: 60000, withCredentials: true, }) // 全局请求拦截器 myAxios.interceptors.request.use( function (config) { // Do something before request is sent return config }, function (error) { // Do something with request error return Promise.reject(error) }, ) // 全局响应拦截器 myAxios.interceptors.response.use( function (response) { const { data } = response // 未登录 if (data.code === 40100) { // 不是获取用户信息的请求,并且用户目前不是已经在用户登录页面,则跳转到登录页面 if ( !response.request.responseURL.includes('user/get/login') && !window.location.pathname.includes('/user/login') ) { message.warning('请先登录') window.location.href = `/user/login?redirect=${window.location.href}` } } return response }, function (error) { // Any status codes that falls outside the range of 2xx cause this function to trigger // Do something with response error return Promise.reject(error) }, ) export default myAxios ``` ## openapi 前端代码生成器 [@umijs/openapi - npm](https://www.npmjs.com/package/@umijs/openapi) 官网自己去学把 ``` npm i --save-dev @umijs/openapi npm i --save-dev tslib ``` ``` export default { requestLibPath: "import request from '@/request'", schemaPath: 'http://localhost:2778/api/v3/api-docs', serversPath: './src', } ``` # 用户登录 img 接口权限 1.未登录也可以使用 2,登录用户才能使用 3,未登录也可以使用,但是登录用户能进行更多操作(比如登录后查看全文) 4,仅管理员才能使用 ### mybatis flex 生成器 不用学看官方就行 然后就是说 这个 entity是数据库与实体类的连接 ### @MapperScan 可能扫描到mapper包 ### Dto就是业务代码互相传递的类 Vo是脱敏的类 ### 用户登录接口 ```java @Override public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) { //校验参数 if (StringUtils.isAnyBlank(userAccount, userPassword)) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不能为空"); } if (userAccount.length() < 4) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过知短"); } if (userPassword.length() < 8) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过知短"); } //加密密码 String encryptPassword = getEncryptPassword(userPassword); //查询用户 QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("userAccount", userAccount); queryWrapper.eq("userPassword", encryptPassword); User user = this.mapper.selectOneByQuery(queryWrapper); ThrowUtils.throwIf(user == null, ErrorCode.PARAMS_ERROR, "用户不存在或密码错误"); //记录用户的登录态 request.getSession().setAttribute(USER_LOGIN_STATE, user); return this.getLoginUserVO(user); } ``` **request.getSession().setAttribute(USER_LOGIN_STATE, user);** 很重要的 种session ## 注解(重要) ### 0先引入依赖 ```xml org.springframework.boot spring-boot-starter-aop ``` ### 1在annotation包下 创建注解类 ```java @Target(ElementType.METHOD) //在方法上的注解 @Retention(RetentionPolicy.RUNTIME) //注解的策略 public @interface AuthCheck { /** * 必须有某个角色 */ String mustRole() default ""; } ``` ### 2 创建一个切面 在aop包里面 ```java package com.zds.zdsai.aop; import com.zds.zdsai.annotation.AuthCheck; import com.zds.zdsai.exception.BusinessException; import com.zds.zdsai.exception.ErrorCode; import com.zds.zdsai.model.entity.User; import com.zds.zdsai.model.enums.UserRoleEnum; import com.zds.zdsai.service.UserService; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @Aspect @Component public class AuthInterceptor { @Resource private UserService userService; /** * 执行拦截 * * @param joinPoint 切入点 * @param authCheck 权限校验注解 */ //切点 -->你在什么时候拦截这个方法 @Around("@annotation(authCheck)") public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable { String mustRole = authCheck.mustRole(); RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); // 当前登录用户 User loginUser = userService.getLoginUser(request); UserRoleEnum mustRoleEnum = UserRoleEnum.getEnumByValue(mustRole); // 不需要权限,放行 if (mustRoleEnum == null) { return joinPoint.proceed(); } // 以下为:必须有该权限才通过 // 获取当前用户具有的权限 UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(loginUser.getUserRole()); // 没有权限,拒绝 if (userRoleEnum == null) { throw new BusinessException(ErrorCode.NO_AUTH_ERROR); } // 要求必须有管理员权限,但用户没有管理员权限,拒绝 if (UserRoleEnum.ADMIN.equals(mustRoleEnum) && !UserRoleEnum.ADMIN.equals(userRoleEnum)) { throw new BusinessException(ErrorCode.NO_AUTH_ERROR); } // 通过权限校验,放行 return joinPoint.proceed(); } } ``` **@Aspect**来定义这是一个切面 **@annotation(authcheck)** --》就是通知 只有在有authcheck注解的 才能进行权限校验 **joinPoint.proceed()**; 放行 ## 前端id与后端id传值不一致 因为后端的int值太tmd大 而前端的js的不支持那么大的 所以会少那么一些 加个样板代码 ```java /** * Spring MVC Json 配置 */ @JsonComponent public class JsonConfig { /** * 添加 Long 转 json 精度丢失的配置 */ @Bean public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { ObjectMapper objectMapper = builder.createXmlMapper(false).build(); SimpleModule module = new SimpleModule(); module.addSerializer(Long.class, ToStringSerializer.instance); module.addSerializer(Long.TYPE, ToStringSerializer.instance); objectMapper.registerModule(module); return objectMapper; } } ``` ## 前端 前端想要获取用户的登陆数据 是不是每次刷新页面 就要写一个fetch函数来获取呢? 不要 我们直接写一个引入pinna > pinna ```ts import { useLoginUserStore } from '@/stores/loginUser.ts' const loginUserStore = useLoginUserStore() loginUserStore.fetchLoginUser() ``` 获取用户 # Ai大模型接入 ## langchain4j > 引入依赖 ```xml dev.langchain4j langchain4j 1.1.0 dev.langchain4j langchain4j-open-ai-spring-boot-starter 1.1.0-beta7 ``` > application.xml ```yml # AI langchain4j: open-ai: chat-model: base-url: https://api.deepseek.com api-key: model-name: deepseek-chat log-requests: true log-responses: true ``` ### 创建接口AiCodeGeneratorService ```java package com.zds.zdsai.ai; import dev.langchain4j.service.SystemMessage; /** * AI代码生成服务 */ public interface AiCodeGeneratorService { /** * 生成HTML代码 * * @param userMessage 用户输入 * @return 代码 */ @SystemMessage(fromResource = "prompt/codegen-html-system-prompt.txt") String generateCode(String userMessage); /** * 生成多文件代码 * * @param userMessage 用户输入 * @return 代码 */ @SystemMessage(fromResource = "prompt/codegen-multi-file-system-prompt.txt") String generateMultiFileCode(String userMessage); } ``` 写提示词 当然 如果词的长度少 你可以不用建立txt 然后就是 写一个代理工厂 来创建实例 ```java package com.zds.zdsai.ai;/* *@auther 郑笃实 *@version 1.0 * */ import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.service.AiServices; import jakarta.annotation.Resource; import org.springframework.context.annotation.Bean; //Ai服务创建工厂 public class AiCodeGeneratorServiceFactory { //指定使用的大模型 @Resource private ChatModel chatModel; /** * 创建AI 代码生成器代码服务 * @return */ @Bean public AiCodeGeneratorService createAiCodeGeneratorService() { return AiServices.create(AiCodeGeneratorService.class, chatModel); } } ``` 是这样的 所以及就获得了一个String的 返回结果 但是问题来了你该如何将String给用户看??直接看??也行 或许 我们可以将String转化为JSON 让ai按照格式生成相应key 这样我们就可以 更好的展示相关的内容了 ```java package com.zds.zdsai.ai.model; import lombok.Data; //Html代码结果 @Data public class HtmlCodeResult { /** * Html代码 */ private String htmlCode; //描述 private String description; } ``` ![image-20251111194720859](F:\记录\项目\image-20251111194720859-1762861641318-1.png) 然后改一下接口的返回值 这样就好了 - method: POST - url: https://api.deepseek.com/chat/completions - headers: [Authorization: Beare...c6], [User-Agent: langchain4j-openai], [Content-Type: application/json] - body: { "model" : "deepseek-chat", "messages" : [ { "role" : "system", "content" : "你是一位资深的 Web 前端开发专家,精通 HTML、CSS 和原生 JavaScript。你擅长构建响应式、美观且代码整洁的单页面网站。\r\n\r\n你的任务是根据用户提供的网站描述,生成一个完整、独立的单页面网站。你需要一步步思考,并最终将所有代码整合到一个 HTML 文件中。\r\n\r\n约束:\r\n1. 技术栈: 只能使用 HTML、CSS 和原生 JavaScript。\r\n2. 禁止外部依赖: 绝对不允许使用任何外部 CSS 框架、JS 库或字体库。所有功能必须用原生代码实现。\r\n3. 独立文件: 必须将所有的 CSS 代码都内联在 `` 标签的 `