mi-assessment/.kiro/steering/development-standards.md
zpc 2c3940895f docs(db): 添加双数据库架构规范文档
- 在开发规范 steering 文件中新增第九节:双数据库架构规范
- 在开发文档中新增第八节:双数据库架构说明
- 明确 Admin 库与 Business 库的职责划分
- 定义配置数据分离规则和 DbContext 映射关系
- 规定跨库访问只读原则
2026-02-20 15:52:28 +08:00

727 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 学业邑规划 - 开发规范
本文档定义了 MiAssessment 项目的开发规范和编码标准,所有开发工作必须遵循这些规范。
## 项目概述
学业邑规划是一个基于多元智能理论的学业规划测评系统,包含微信小程序和后台管理系统。
### 相关文档
- 需求文档: `docs/需求文档.md`
- 数据库设计: `docs/数据库设计文档.md`
- 开发文档: `docs/开发文档.md`
- 题库资料: `docs/题库和结论/`
- UI 设计图: `docs/设计图/`
- UI 切图: `docs/切图/`
## 一、技术栈
| 层级 | 技术 |
|------|------|
| 后端框架 | .NET 10 Web API (C#) |
| 数据库 | SQL Server 2022 |
| ORM | Entity Framework Core |
| 缓存 | Redis |
| 接口风格 | RPC 风格(仅 GET / POST 请求) |
| 小程序前端 | UniApp + Vue 3 |
| 后台管理前端 | Vue 3 + TypeScript + Vite |
## 二、项目结构
### 整体结构
```
mi-assessment/
├── docs/ # 文档资料
│ ├── 需求文档.md
│ ├── 数据库设计文档.md
│ ├── 开发文档.md
│ ├── 开发规范/ # 详细编码规范
│ ├── 设计图/ # UI 设计图
│ ├── 切图/ # UI 切图资源
│ └── 题库和结论/ # 测评题库与报告模板
├── server/MiAssessment/ # 后端服务
└── uniapp/ # 小程序前端
```
### 后端项目结构
```
server/MiAssessment/src/
├── MiAssessment.Api/ # 小程序 API 接口
├── MiAssessment.Admin/ # 后台管理系统基础模块
│ └── admin-web/ # 后台管理前端 (Vue 3)
├── MiAssessment.Admin.Business/ # 后台业务模块(主要开发区域)
├── MiAssessment.Core/ # 核心业务逻辑
├── MiAssessment.Infrastructure/ # 基础设施
└── MiAssessment.Model/ # 数据模型
```
### Admin.Business 项目结构
```
MiAssessment.Admin.Business/
├── Controllers/ # 控制器API 入口)
├── Services/ # 业务服务
│ └── Interfaces/ # 服务接口
├── Models/ # 请求/响应模型DTO
│ ├── Common/ # 通用模型
│ └── {Module}/ # 各模块模型
├── Entities/ # 数据库实体类
├── Data/ # 数据库上下文
└── Attributes/ # 自定义特性
```
## 三、命名规范
### 3.1 数据库命名
| 类型 | 规范 | 示例 |
|------|------|------|
| 表名 | snake_case小写下划线 | `users`, `assessment_records` |
| 字段名 | PascalCase | `UserId`, `CreateTime` |
| 主键 | `Id` (bigint 自增) | `Id` |
| 外键 | `{表名}Id` | `UserId`, `OrderId` |
| 时间字段 | `CreateTime`, `UpdateTime` | - |
| 状态字段 | `Status` | - |
| 软删除 | `IsDeleted` (bit) | - |
### 3.2 C# 代码命名
| 类型 | 规范 | 示例 |
|------|------|------|
| 命名空间 | PascalCase | `MiAssessment.Admin.Business.Services` |
| 类名 | PascalCase | `UserService`, `OrderController` |
| 接口 | I + PascalCase | `IUserService`, `IOrderService` |
| 方法 | PascalCase + Async 后缀 | `GetUserListAsync`, `CreateOrderAsync` |
| 属性 | PascalCase | `UserId`, `CreateTime` |
| 私有字段 | _camelCase | `_userService`, `_dbContext` |
| 参数 | camelCase | `userId`, `request` |
| 常量 | PascalCase | `MaxPageSize`, `DefaultStatus` |
### 3.3 文件命名
| 类型 | 规范 | 示例 |
|------|------|------|
| 实体类 | 单数形式 | `User.cs`, `Order.cs` |
| 服务接口 | I + 服务名 | `IUserService.cs` |
| 服务实现 | 服务名 | `UserService.cs` |
| 控制器 | 模块名 + Controller | `UserController.cs` |
| DTO 模型 | 功能 + Request/Dto | `CreateUserRequest.cs`, `UserDto.cs` |
### 3.4 前端命名 (Vue/JavaScript)
| 类型 | 规范 | 示例 |
|------|------|------|
| 组件文件 | PascalCase | `UserList.vue`, `OrderDetail.vue` |
| 组合式函数 | use + camelCase | `useUserList.js`, `useAuth.js` |
| 工具函数 | camelCase | `formatDate.js`, `request.js` |
| 变量/函数 | camelCase | `userList`, `handleSubmit` |
| 常量 | UPPER_SNAKE_CASE | `API_BASE_URL`, `MAX_PAGE_SIZE` |
| CSS 类名 | kebab-case | `user-list`, `order-card` |
## 四、代码规范
### 4.1 实体类规范
```csharp
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MiAssessment.Admin.Business.Entities;
/// <summary>
/// 用户表
/// </summary>
[Table("users")]
public class User
{
/// <summary>
/// 主键ID
/// </summary>
[Key]
public long Id { get; set; }
/// <summary>
/// 用户UID
/// </summary>
[Required]
[MaxLength(6)]
public string Uid { get; set; } = null!;
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdateTime { get; set; }
/// <summary>
/// 软删除标记
/// </summary>
public bool IsDeleted { get; set; }
}
```
**要点**
- 使用 `[Table("表名")]` 指定数据库表名
- 使用 `[Key]` 标记主键
- 使用 `[Required]` 标记必填字段
- 使用 `[MaxLength(n)]` 限制字符串长度
- 所有属性必须有 XML 注释
- 字符串属性使用 `= null!` 初始化
### 4.2 服务接口规范
```csharp
namespace MiAssessment.Admin.Business.Services.Interfaces;
/// <summary>
/// 用户服务接口
/// </summary>
public interface IUserService
{
/// <summary>
/// 获取用户列表
/// </summary>
Task<PagedResult<UserDto>> GetUserListAsync(UserQueryRequest request);
/// <summary>
/// 获取用户详情
/// </summary>
Task<UserDetailDto?> GetUserDetailAsync(long id);
/// <summary>
/// 更新用户状态
/// </summary>
Task<bool> UpdateUserStatusAsync(long id, int status);
}
```
### 4.3 服务实现规范
```csharp
namespace MiAssessment.Admin.Business.Services;
/// <summary>
/// 用户服务实现
/// </summary>
public class UserService : IUserService
{
private readonly AdminBusinessDbContext _dbContext;
private readonly ILogger<UserService> _logger;
public UserService(
AdminBusinessDbContext dbContext,
ILogger<UserService> logger)
{
_dbContext = dbContext;
_logger = logger;
}
/// <inheritdoc />
public async Task<PagedResult<UserDto>> GetUserListAsync(UserQueryRequest request)
{
var query = _dbContext.Users
.Where(u => !u.IsDeleted);
// 条件筛选
if (!string.IsNullOrEmpty(request.Phone))
{
query = query.Where(u => u.Phone.Contains(request.Phone));
}
// 获取总数
var total = await query.CountAsync();
// 分页查询
var items = await query
.OrderByDescending(u => u.CreateTime)
.Skip(request.Skip)
.Take(request.PageSize)
.Select(u => new UserDto
{
Id = u.Id,
Uid = u.Uid,
// ...
})
.ToListAsync();
return new PagedResult<UserDto>(items, total, request.Page, request.PageSize);
}
}
```
### 4.4 控制器规范
```csharp
namespace MiAssessment.Admin.Business.Controllers;
/// <summary>
/// 用户管理控制器
/// </summary>
[Route("api/admin/user")]
public class UserController : BusinessControllerBase
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
/// <summary>
/// 获取用户列表
/// </summary>
[HttpGet("getList")]
public async Task<IActionResult> GetList([FromQuery] UserQueryRequest request)
{
var result = await _userService.GetUserListAsync(request);
return Ok(result);
}
/// <summary>
/// 更新用户状态
/// </summary>
[HttpPost("updateStatus")]
public async Task<IActionResult> UpdateStatus([FromBody] UpdateStatusRequest request)
{
var success = await _userService.UpdateUserStatusAsync(request.Id, request.Status);
return success ? Ok() : Error(ErrorCodes.UserNotFound, "用户不存在");
}
}
```
## 五、API 接口规范
### 5.1 接口风格
- **仅使用 GET 和 POST 请求**
- GET查询操作参数通过 Query String 传递
- POST新增、修改、删除操作参数通过 Request Body (JSON) 传递
### 5.2 路由规范
```
小程序 API: /api/{module}/{action}
后台管理 API: /api/admin/{module}/{action}
```
| 操作 | 方法 | 路由示例 |
|------|------|----------|
| 获取列表 | GET | `/api/admin/user/getList` |
| 获取详情 | GET | `/api/admin/user/getDetail?id=1` |
| 创建 | POST | `/api/admin/user/create` |
| 更新 | POST | `/api/admin/user/update` |
| 删除 | POST | `/api/admin/user/delete` |
| 更新状态 | POST | `/api/admin/user/updateStatus` |
| 更新排序 | POST | `/api/admin/user/updateSort` |
### 5.3 响应格式
```json
{
"code": 0,
"message": "success",
"data": {}
}
```
- `code`: 0 表示成功,非 0 表示错误
- `message`: 提示信息
- `data`: 业务数据
### 5.4 分页响应
```json
{
"code": 0,
"message": "success",
"data": {
"list": [],
"total": 100,
"page": 1,
"pageSize": 20,
"totalPages": 5
}
}
```
## 六、错误码规范
| 范围 | 说明 |
|------|------|
| 0 | 成功 |
| 1000-1999 | 通用错误 |
| 2000-2999 | 业务错误 |
| 3000-3099 | 配置模块错误 |
| 3100-3199 | 内容模块错误 |
| 3200-3299 | 测评模块错误 |
| 3300-3399 | 用户模块错误 |
| 3400-3499 | 订单模块错误 |
| 3500-3599 | 规划师模块错误 |
| 3600-3699 | 分销模块错误 |
| 5000-5999 | 系统错误 |
## 七、注释规范
### 7.1 XML 注释 (C#)
所有公开的类、方法、属性必须有 XML 注释:
```csharp
/// <summary>
/// 获取用户列表
/// </summary>
/// <param name="request">查询参数</param>
/// <returns>分页用户列表</returns>
public async Task<PagedResult<UserDto>> GetUserListAsync(UserQueryRequest request)
```
### 7.2 JSDoc 注释 (JavaScript)
```javascript
/**
* 获取用户列表
* @param {Object} params - 查询参数
* @param {number} [params.page] - 页码
* @param {number} [params.pageSize] - 每页数量
* @param {string} [params.phone] - 手机号
* @returns {Promise<Object>} 分页用户列表
*/
export async function getUserList(params) {
// ...
}
```
### 7.3 代码注释
- 使用中文注释
- 复杂逻辑需要添加说明注释
- 避免无意义的注释
## 八、数据库操作规范
### 8.1 查询规范
```csharp
// 始终过滤软删除记录
var query = _dbContext.Users.Where(u => !u.IsDeleted);
// 使用 AsNoTracking 提高只读查询性能
var users = await _dbContext.Users
.AsNoTracking()
.Where(u => !u.IsDeleted)
.ToListAsync();
```
### 8.2 软删除
```csharp
// 删除操作使用软删除
public async Task<bool> DeleteAsync(long id)
{
var entity = await _dbContext.Users.FindAsync(id);
if (entity == null) return false;
entity.IsDeleted = true;
entity.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
return true;
}
```
### 8.3 更新时间
```csharp
// 更新操作自动设置 UpdateTime
entity.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
```
## 九、双数据库架构规范
### 9.1 数据库划分
本项目采用双数据库架构,严格区分后台管理数据与业务数据:
| 数据库 | 名称 | 职责 |
|--------|------|------|
| Admin 库 | `MiAssessment_Admin` | 后台管理系统自身运行所需数据RBAC、审计、运营配置 |
| Business 库 | `MiAssessment_Business` | C端用户产生的或直接服务C端的业务数据 |
### 9.2 Admin 库表清单
| 表名 | 说明 |
|------|------|
| `admin_users` | 后台管理员 |
| `roles` | 角色 |
| `menus` | 菜单 |
| `departments` | 部门 |
| `permissions` | 权限 |
| `role_menus` | 角色-菜单关联 |
| `role_permissions` | 角色-权限关联 |
| `user_roles` | 用户-角色关联 |
| `operation_logs` | 操作日志 |
| `refresh_tokens` | 刷新令牌 |
| `admin_configs` | 运营配置(支付、小程序、上传、短信等) |
| `dict_types` | 字典类型 |
| `dict_items` | 字典项 |
### 9.3 Business 库表清单
| 表名 | 说明 |
|------|------|
| `configs` | 业务配置(测评价格、佣金比例、提现限额、联系方式、协议等) |
| `banners` | 轮播图 |
| `promotions` | 宣传图 |
| `assessment_types` | 测评类型 |
| `questions` | 题目 |
| `report_categories` | 报告分类 |
| `question_category_mappings` | 题目-分类映射 |
| `report_conclusions` | 报告结论 |
| `assessment_records` | 测评记录 |
| `assessment_answers` | 测评答案 |
| `assessment_results` | 测评结果 |
| `users` | C端用户 |
| `orders` | 订单 |
| `planners` | 规划师 |
| `planner_bookings` | 规划预约 |
| `invite_codes` | 邀请码 |
| `commissions` | 佣金记录 |
| `withdrawals` | 提现记录 |
| `business_pages` | 业务页面 |
### 9.4 配置数据分离规则
| 配置类型 | 所在库 | 表名 | 示例 Key |
|----------|--------|------|----------|
| 运营配置 | Admin 库 | `admin_configs` | `weixinpay_setting`, `miniprogram_setting`, `upload_setting`, `sms_setting`, `app_setting`, `base` |
| 业务配置 | Business 库 | `configs` | `assessment_price`, `commission_rate_direct`, `commission_rate_indirect`, `withdraw_min_amount`, `contact_wechat`, `user_agreement`, `privacy_policy` |
### 9.5 DbContext 映射
| DbContext | 所在项目 | 连接数据库 | 读写权限 | 用途 |
|-----------|----------|------------|----------|------|
| `AdminDbContext` | MiAssessment.Admin | Admin 库 | 读写 | 后台管理系统主上下文 |
| `AdminBusinessDbContext` | MiAssessment.Admin.Business | Business 库 | 读写 | 后台管理业务数据 |
| `AdminConfigDbContext` | MiAssessment.Admin.Business | Admin 库 | 只读 | Business 项目读取运营配置 |
| `AdminConfigReadDbContext` | MiAssessment.Model | Admin 库 | 只读 | Core/API 项目读取运营配置 |
| `MiAssessmentDbContext` | MiAssessment.Model | Business 库 | 读写 | 小程序 API 主上下文 |
### 9.6 跨库访问规则
1. **Business 项目对 Admin 库只读**:通过 `AdminConfigDbContext` 读取运营配置,禁止写入
2. **Core/API 项目对 Admin 库只读**:通过 `AdminConfigReadDbContext` 读取运营配置,禁止写入
3. **Admin 库的写操作只通过 Admin 项目进行**:所有运营配置的增删改必须通过 `AdminDbContext`
4. **连接字符串命名**Admin 库使用 `AdminConnection`Business 库使用 `DefaultConnection`
### 9.7 新增配置项规则
- 如果配置是**运营/系统级别**(支付密钥、上传凭证、短信配置等)→ 存入 Admin 库 `admin_configs`
- 如果配置是**业务级别**(价格、佣金比例、协议内容等)→ 存入 Business 库 `configs`
- 新增 DbContext 时必须明确标注读写权限,跨库上下文必须标注 `只读`
## 十、状态值定义
### 9.1 通用状态
| 值 | 说明 |
|----|------|
| 0 | 禁用/下线 |
| 1 | 启用/正常 |
### 9.2 订单状态
| 值 | 说明 |
|----|------|
| 1 | 待支付 |
| 2 | 已支付 |
| 3 | 已完成 |
| 4 | 退款中 |
| 5 | 已退款 |
| 6 | 已取消 |
### 9.3 用户等级
| 值 | 说明 |
|----|------|
| 1 | 普通用户 |
| 2 | 合伙人 |
| 3 | 渠道合伙人 |
## 十一、测试规范
### 10.1 测试框架
- 单元测试xUnit + Moq
- 属性测试FsCheck
### 10.2 测试命名
```csharp
[Fact]
public async Task GetUserListAsync_WithValidRequest_ReturnsPagedResult()
{
// Arrange
// Act
// Assert
}
```
### 10.3 属性测试注释
```csharp
/// <summary>
/// Property: 分页查询返回的记录数不超过 PageSize
/// **Validates: Requirements 9.1**
/// </summary>
[Property]
public Property PaginationReturnsCorrectCount()
{
// ...
}
```
## 十二、Git 提交规范
### 11.1 提交信息格式
```
<type>(<scope>): <subject>
<body>
```
### 11.2 Type 类型
| Type | 说明 |
|------|------|
| feat | 新功能 |
| fix | 修复 bug |
| docs | 文档更新 |
| style | 代码格式 |
| refactor | 重构 |
| test | 测试 |
| chore | 构建/工具 |
### 11.3 示例
```
feat(user): 添加用户列表分页查询功能
- 实现 GetUserListAsync 方法
- 支持按手机号、昵称筛选
- 添加分页参数验证
```
## 十三、前端开发规范(小程序)
### 12.1 Vue 组件规范
```vue
<script setup>
/**
* 用户列表组件
*/
import { ref, onMounted, defineProps, defineEmits } from 'vue'
import { getUserList } from '@/api/user'
// Props
const props = defineProps({
status: {
type: Number,
default: null
}
})
// Emits
const emit = defineEmits(['select'])
// State
const loading = ref(false)
const userList = ref([])
// Methods
async function fetchData() {
loading.value = true
try {
const res = await getUserList({ status: props.status })
userList.value = res.data.list
} finally {
loading.value = false
}
}
// Lifecycle
onMounted(() => {
fetchData()
})
</script>
<template>
<view class="user-list">
<!-- 内容 -->
</view>
</template>
<style scoped lang="scss">
.user-list {
/* 样式 */
}
</style>
```
### 12.2 API 请求规范
```javascript
// api/user.js
import { get, post } from '@/api/request'
/**
* 获取用户列表
* @param {Object} params - 查询参数
* @returns {Promise<Object>}
*/
export function getUserList(params) {
return get('/user/getList', params)
}
/**
* 更新用户状态
* @param {number} id - 用户ID
* @param {number} status - 状态
* @returns {Promise<Object>}
*/
export function updateUserStatus(id, status) {
return post('/user/updateStatus', { id, status })
}
```
### 12.3 常量定义规范
```javascript
// constants/user.js
/** 用户等级 */
export const USER_LEVEL = {
NORMAL: 1, // 普通用户
PARTNER: 2, // 合伙人
CHANNEL: 3 // 渠道合伙人
}
/** 订单状态 */
export const ORDER_STATUS = {
PENDING: 1, // 待支付
PAID: 2, // 已支付
COMPLETED: 3, // 已完成
REFUNDING: 4, // 退款中
REFUNDED: 5, // 已退款
CANCELLED: 6 // 已取消
}
```