원클릭으로
domestic-stack
写 uniapp / 微信小程序 / SpringBoot 代码时使用。贴合国内主流技术栈的实战规范。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
写 uniapp / 微信小程序 / SpringBoot 代码时使用。贴合国内主流技术栈的实战规范。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
做 Cocos Creator 多机型/多分辨率适配时使用。Canvas、Widget、安全区。
Cocos Creator 用 AssetBundle 做分包/远程资源时使用。加载、释放、依赖、缓存。
优化 Cocos Creator 渲染性能时使用。合批、图集、动静分离、Label。
给 Cocos Creator 原生包做热更新时使用。version manifest、增量、校验、回滚。
写 Cocos Creator 动效/动画时使用。tween、Animation、Spine、性能与清理。
做 Cocos Creator 大量条目列表时使用。虚拟列表、节点复用。
| name | domestic-stack |
| description | 写 uniapp / 微信小程序 / SpringBoot 代码时使用。贴合国内主流技术栈的实战规范。 |
| category | china |
| tags | ["uniapp","小程序","springboot"] |
规则: 微信小程序和 uniapp 运行在专属运行时,没有 window、document、localStorage 等 DOM/BOM 对象;setData 是性能敏感操作,频繁调用或传大对象会直接卡顿;整包 + 分包体积有硬性上限(主包 2 MB,总包 20 MB)。
为什么: AI 训练数据以 web 代码为主,极容易写出 document.querySelector、window.location.href、localStorage.setItem 这类在小程序里直接报 ReferenceError 的代码。对 setData 的误用更隐蔽:AI 常常在循环里每次更新一个字段就调用一次 setData,或者把整个巨型列表塞进去,运行时帧率立刻掉到个位数。
怎么做:
wx.setStorageSync / uni.setStorageSync,路由用 wx.navigateTo / uni.navigateTo,DOM 操作改用数据绑定驱动视图。setData,用 path 精确更新而非替换整个对象(见正例)。规则: uniapp 跨 APP / 微信小程序 / H5 时,平台差异必须用 #ifdef APP-PLUS / #ifdef MP-WEIXIN / #ifdef H5 等条件编译块处理,不写死只在单端能跑的逻辑。
为什么: AI 在生成 uniapp 代码时经常直接调用 wx.* API 而不加条件编译,在 APP 端或 H5 端运行时这些调用会静默失败或抛异常。反过来也会犯:只写了 plus.* 的 APP 专属逻辑,发布 H5 版时整块功能缺失。这类 bug 在单端开发阶段完全看不出来,到多端上线时才暴露。
怎么做:
/* #ifdef MP-WEIXIN */ 包裹对应 CSS。规则: 微信登录必须走 wx.login 获取 code,再由后端调用 code2session 换取 openid 和 session_key;openid 和 session_key 不得存在前端,不得绕过授权步骤直接用 union_id 或手机号作为身份标识。
为什么: AI 经常写出把 session_key 存进 wx.setStorageSync 的代码——这违反微信安全规范,session_key 会失效且不能直接暴露给客户端。还有一种常见错误:前端直接拿 getUserInfo 返回的 openid(旧接口早已废弃)当登录凭证,绕过后端校验,既不安全也不符合现行微信能力。
怎么做:
wx.login → 拿到 code → 发给自己后端。code2session 接口,换回 openid / session_key,生成自己的登录态 token 返回前端。getPhoneNumber 组件 + 后端解密,不走已废弃的 getUserInfo。规则: Controller 只做参数接收与响应封装,业务逻辑全部下沉到 Service,数据库操作放 Mapper / Repository;统一异常处理用 @ControllerAdvice,参数校验用 @Valid + ConstraintValidator,不在 Controller 里写业务代码。
为什么: AI 写 SpringBoot 时极其容易把业务逻辑堆在 Controller 里——直接在 @GetMapping 方法里查库、算价格、拼 SQL——表面上能跑,但单元测试无法覆盖(Controller 层的业务很难 mock),复用性为零,后续需求一变就到处改。参数校验也常被忽略,AI 会直接用 if (name == null) 手写 if-else,而不是用注解在入口统一拦截。
怎么做:
ResponseEntity / 统一 Result<T>)。@NotNull、@Size、@Pattern 等注解 + @Valid,统一在 @ControllerAdvice 里捕获 MethodArgumentNotValidException。RuntimeException,在 @ControllerAdvice 里集中处理,返回标准错误格式。规则: 国内上线项目必须具备 ICP 备案、隐私政策、必要的数据合规措辞;npm 依赖用淘宝镜像(registry.npmmirror.com),Maven 依赖用阿里云镜像(maven.aliyun.com);静态资源走国内 CDN,不直接引用境外 CDN 地址。
为什么: AI 生成的项目配置默认指向境外镜像(registry.npmjs.org、repo.maven.apache.org),在国内 CI/CD 环境里拉包极慢甚至超时,导致构建失败。更严重的是 AI 生成的 HTML 模板常常引用 cdn.jsdelivr.net 或 unpkg.com,这些域名在国内访问不稳定,直接造成生产页面资源加载失败。隐私合规方面,AI 不会自动提醒你"需要 ICP 备案"或"用户协议需要说明数据收集范围",这些遗漏可能导致应用被下架。
怎么做:
.npmrc 或 package.json 的 publishConfig 指向淘宝镜像;settings.xml 的 <mirror> 指向阿里云。// 反例 — 每次只改一个字段,却替换整个列表对象,触发全量 diff + 全量渲染
Page({
data: {
list: [
{ id: 1, name: '商品A', count: 0 },
{ id: 2, name: '商品B', count: 0 },
]
},
addCount(index) {
const list = this.data.list
list[index].count += 1
this.setData({ list }) // ❌ 传整个数组,无论列表多大都全量更新
}
})
// 正例 — 用 path 语法精确更新变化的字段,渲染开销最小
Page({
data: {
list: [
{ id: 1, name: '商品A', count: 0 },
{ id: 2, name: '商品B', count: 0 },
]
},
addCount(index) {
const key = `list[${index}].count`
this.setData({
[key]: this.data.list[index].count + 1 // ✅ 只更新变化的路径,性能最优
})
}
})
// 反例 — 直接调用 wx.* API,APP 端和 H5 端运行时报错
export function saveToken(token) {
wx.setStorageSync('token', token) // ❌ APP 端没有 wx 对象,直接 ReferenceError
}
// 正例 — 用 uni.* 统一 API + 条件编译处理真正有差异的部分
export function saveToken(token) {
uni.setStorageSync('token', token) // ✅ uni.* 在所有端均可用
// #ifdef APP-PLUS
// APP 端额外写入原生 keychain(示例)
plus.storage.setItem('token', token)
// #endif
}
// 反例 — Controller 直接查库、写业务、手写 if-else 校验,什么都往里塞
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderMapper orderMapper;
@PostMapping("/create")
public Map<String, Object> createOrder(@RequestBody Map<String, Object> body) {
// ❌ 参数校验手写 if-else
if (body.get("userId") == null || body.get("amount") == null) {
return Map.of("code", 400, "msg", "参数缺失");
}
// ❌ 业务逻辑直接在 Controller 里算
double amount = Double.parseDouble(body.get("amount").toString());
if (amount <= 0) {
return Map.of("code", 400, "msg", "金额非法");
}
// ❌ 直接调 Mapper,绕过 Service 层
orderMapper.insert(body.get("userId"), amount);
return Map.of("code", 200, "msg", "创建成功");
}
}
// 正例 — Controller 只接参/返响应;业务在 Service;校验用注解统一处理
// DTO:用注解声明校验规则
public class CreateOrderRequest {
@NotNull(message = "userId 不能为空")
private Long userId;
@Positive(message = "金额必须大于 0")
private Double amount;
// getter / setter 省略
}
// Controller:只负责接参、调 Service、封装响应
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/create")
public Result<Long> createOrder(@Valid @RequestBody CreateOrderRequest req) {
// ✅ 校验由 @Valid 在入口统一拦截,Controller 无需 if-else
Long orderId = orderService.createOrder(req);
return Result.ok(orderId);
}
}
// Service:业务逻辑全在这里
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Override
public Long createOrder(CreateOrderRequest req) {
// ✅ 业务逻辑集中,可独立单测
Order order = new Order(req.getUserId(), req.getAmount());
orderMapper.insert(order);
return order.getId();
}
}
// 全局异常处理:统一捕获校验异常,返回标准格式
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValidation(MethodArgumentNotValidException ex) {
String msg = ex.getBindingResult().getFieldErrors()
.stream().map(FieldError::getDefaultMessage)
.collect(Collectors.joining("; "));
return Result.fail(400, msg); // ✅ 统一错误格式,前端好处理
}
}
window、document、localStorage——已换成小程序/uniapp 对应的 API。setData 调用都经过合并,且使用 path 语法只更新变化的字段,没有在循环里多次调用。#ifdef 条件编译包裹,没有写死单端 API。@ControllerAdvice 统一处理,没有散落的手写 if-else 校验。package.json / settings.xml 已配置国内镜像源,前端资源没有引用境外 CDN 链接。