1 changed files with 234 additions and 0 deletions
@ -0,0 +1,234 @@
|
||||
# 教研 |
||||
|
||||
## 代码结构 |
||||
|
||||
``` |
||||
├─ Directory.Build.props(通用的配置默认给所有项目加载) |
||||
├─ NuGet.Config(Nuget配置) |
||||
├─ Office2Html(文件转换) |
||||
├─ Office2Pdf(文件转换) |
||||
├─ common.props(通用配置) |
||||
├─ host |
||||
│ └─ Sh.Platform.HttpApi.Host(启动项) |
||||
├─ plugins |
||||
│ ├─ Menu(菜单相关) |
||||
│ ├─ WeChat(微信相关) |
||||
│ ├─ contest(教研活动、作品提交及评审) |
||||
│ ├─ fangan(方案设计、主题作业、通知、家校相关) |
||||
│ ├─ file(文件上传、使用统计及配置) |
||||
│ ├─ jiaoyan(题库、组卷、资源库、直播、二次备课、教研活动查看等教研相关) |
||||
│ └─ school(学校、学生、年级班级、目录和知识点等基础数据) |
||||
├─ src(主模块) |
||||
│ ├─ 依赖了第三方库小程序模块,并重写了绑定流程 |
||||
│ ├─ 依赖了各种基础模块,比如:用户、角色、权限、租户 |
||||
│ ├─ 重写了用户密码加密方式 |
||||
│ ├─ 添加了用户绑定邮箱、发送验证码、重置密码登录功能 |
||||
│ ├─ 添加了监护人绑定功能(小程序) |
||||
``` |
||||
|
||||
## 各种地址 |
||||
|
||||
[产品原型地址](https://2njmk5.axshare.com/#id=37v3jm&p=%E6%95%99%E7%A0%94%E9%9B%B7%E8%BE%BE%E5%85%A5%E5%8F%A3&g=1) |
||||
|
||||
[创小智测试地址](http://cheng.xmsceshi.xyxmt.cn/) |
||||
|
||||
- 学校:`ceshi` `1q2w3E*` |
||||
- 教师(刘德华):`0014` `1q2w3E*` |
||||
- 学生(宁红伟):`NHW20220004` `1q2w3E*` |
||||
|
||||
[教研测试地址](http://cheng.xmsceshi.xyxmt.cn/jiaoyan/index.html#/passport/login) |
||||
|
||||
- admin:`admin` `1q2w3E*` |
||||
|
||||
## 待做工作 |
||||
|
||||
- 单元测试 |
||||
- 目前已经搭建了文件、教研、方案模块的测试 |
||||
- 文件整体模块大体完成了 `70%`,只是一对一测试 |
||||
- 教研完成了资源库的测试用例编写,其他还需完善 |
||||
- 方案只搭建了测试项目 |
||||
- 可以通过`EasyAbp Helper`根据模块名称创建模块之后,将测试项目直接复制过去,这样就避免了手动创建的麻烦 |
||||
|
||||
## 注意事项 |
||||
|
||||
### 1. 分支 |
||||
|
||||
`jiaoyan-dev-zongheshijian-file` 分支目前包含有文件统计服务及单元测试。如果要合并的话,需要将`jiaoyan-dev-zongheshijian`分支先合并到`jiaoyan-dev-zongheshijian-file`分支,之后再合并过去,然后删除文件分支。 |
||||
|
||||
### 2. 基础用户 |
||||
|
||||
由于历史原因,`contest`和`school`是两套系统结合起来的,而两套系统中都有用户,所幸的是因为 Abp 用户模块的独立性,只需要将扩展用户表对应起来就可以。`school`教师对应`contest`中的学生,学校两边相对应,**请注意同步数据**。 |
||||
|
||||
### 3. 微信小程序用户 |
||||
|
||||
这一块交互较为复杂混乱。 |
||||
|
||||
### 4. 文件实体配置 |
||||
|
||||
大体上已经配置完成了,现在的配置的方式类似于单向链表。 |
||||
|
||||
例如下面的配置,根节点是教研活动,而活动项不需要统计文件,但还是需要显示是哪个活动项的,就可以使用 `AddEntityConfig` 进行配置。 |
||||
|
||||
如果需要统计该实体包含哪些文件就需要使用 `AddHasUploadContentEntity` 进行配置。 |
||||
|
||||
```csharp |
||||
Configure<FileServiceOptions>(options => |
||||
{ |
||||
options.AddEntityConfig<Contest>(config => |
||||
{ |
||||
config.Root("教研活动", contest => contest.ContestName); |
||||
}); |
||||
|
||||
options.AddEntityConfig<ContestItem>(config => |
||||
{ |
||||
config.Parent<Contest>(contestItem => contestItem.ContestId); |
||||
config.SetAliasAndTitle("活动项", contestItem => contestItem.ContestItemName); |
||||
}); |
||||
|
||||
options.AddHasUploadContentEntity<ContestItemWorkContent>(config => |
||||
{ |
||||
config.Parent<ContestItem>(content => content.ContestItemId); |
||||
}); |
||||
|
||||
options.AddHasUploadContentEntity<ContestItemWorkQuestion>(config => |
||||
{ |
||||
config.Parent<ContestItem>(content => content.ContestItemId); |
||||
}); |
||||
}); |
||||
``` |
||||
|
||||
除了根节点之外,都需要配置向上查找的主键 Id,例如活动项需要向上查找活动项,需要配置 `config.Parent<Contest>(contestItem => contestItem.ContestId)` 程序根据 `ContestId` 去查找是哪个教研活动。 |
||||
|
||||
如果需要显示多层目录,比如 |
||||
|
||||
``` |
||||
教研活动:第一届教师节 |
||||
活动项:数学教师活动项 |
||||
``` |
||||
|
||||
则需要配置 `SetAliasAndTitle`。 |
||||
|
||||
下面这行代码的意思是设置别名(活动项),设置标题,从查找出来的实体中取 `ContestItemName` 作为标题。 |
||||
|
||||
`config.SetAliasAndTitle("活动项", contestItem => contestItem.ContestItemName)` |
||||
|
||||
# 投票 |
||||
|
||||
## 代码结构 |
||||
|
||||
``` |
||||
├─ Directory.Build.props(通用的配置默认给所有项目加载) |
||||
├─ Files(文件上传相关) |
||||
├─ Menu(权限菜单相关) |
||||
├─ NuGet.Config(Nuget配置) |
||||
├─ VoteBase(基础模块、包含选手、选手列表、活动、排行、报名等) |
||||
├─ VoteLiWu(收费模式、包含装饰品、支付、包装后的选手列表等) |
||||
├─ WeChat(微信交互相关) |
||||
├─ common.props(通用配置) |
||||
``` |
||||
|
||||
## 各种地址 |
||||
|
||||
[测试后台](http://toupiao.ceshi.sanhexinxi.com/admin/index.html#/passport/login) |
||||
[安全日志路径](http://toupiao.ceshi.sanhexinxi.com/admin/index.html#/ng/vote/sys/safelog) |
||||
[审计日志路径](http://toupiao.ceshi.sanhexinxi.com/admin/index.html#/ng/vote/sys/auditlog) |
||||
|
||||
- admin:`admin` `1q2w3E*` |
||||
|
||||
[合作商原型](https://wtyutr.axshare.com/#id=sb3hr4&p=%E7%99%BB%E5%BD%95%E9%A1%B5%E9%9D%A2) |
||||
|
||||
[手机端原型](https://6n1rfl.axshare.com/#id=rhnvd7&p=%E4%BF%AE%E6%94%B9%E8%AE%B0%E5%BD%95&g=1) |
||||
|
||||
[后台原型](https://dzv7q8.axshare.com/#id=ft4j3k&p=%E4%BF%AE%E6%94%B9%E8%AE%B0%E5%BD%95&g=1) |
||||
|
||||
## 注意事项 |
||||
|
||||
### 1. 自定义投票规则验证提供者 |
||||
|
||||
通过继承 `RuleValidationProvider` 抽象类快速实现投票规则验证。例如,一天只有特定时间才能进行投票 |
||||
|
||||
```csharp |
||||
public class LimitTimeRuleValidationProvider : RuleValidationProvider |
||||
{ |
||||
public const string ProviderName = "LimitRangeTime"; |
||||
public const string StartTime = nameof(StartTime); |
||||
public const string EndTime = nameof(EndTime); |
||||
|
||||
public LimitTimeRuleValidationProvider(IServiceProvider serviceProvider) : base(serviceProvider) |
||||
{ |
||||
|
||||
} |
||||
|
||||
public override string Name => ProviderName; |
||||
|
||||
public override Task ValidateAsync(RuleValidationContext context, CancellationToken cancellationToken = default) |
||||
{ |
||||
var startTime = TimeOnly.FromDateTime(context.GetProperty<DateTime>(StartTime)); |
||||
var endTime = TimeOnly.FromDateTime(context.GetProperty<DateTime>(EndTime)); |
||||
|
||||
if (startTime > endTime) |
||||
{ |
||||
Logger.LogWarning($"因开始时间({startTime})大于结束时间({endTime}),跳过此验证!"); |
||||
return Task.CompletedTask; |
||||
} |
||||
|
||||
var currentTime = TimeOnly.FromDateTime(DateTime.Now); |
||||
|
||||
if (currentTime > startTime && currentTime < endTime) |
||||
{ |
||||
throw new UserFriendlyException($"当前时间段({startTime}-{endTime})不允许投票!"); |
||||
} |
||||
|
||||
return Task.CompletedTask; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
之后,创建一个继承`RuleDefinitionProvider`的类,如下所示 |
||||
|
||||
```csharp |
||||
public class BaseRuleDefinitionProvider : RuleDefinitionProvider |
||||
{ |
||||
public override void Define(IRuleDefinitionContext context) |
||||
{ |
||||
var group = context.AddGroup("Rule.Common.Group", "通用模块规则组"); |
||||
|
||||
group |
||||
.AddRuleDefinition( |
||||
name: LimitTimeRuleValidationProvider.ProviderName, |
||||
displayName: "禁止投票时间段") |
||||
.WithProperty( |
||||
propertyName: LimitTimeRuleValidationProvider.StartTime, |
||||
defaultValue: DateTime.Parse("00:00:00")) |
||||
.WithProperty( |
||||
propertyName: LimitTimeRuleValidationProvider.EndTime, |
||||
defaultValue: DateTime.Parse("06:00:00")); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
你需要在 `Define` 方法中添加你的规则验证定义提供者,并添加相关名称及默认值。 |
||||
|
||||
参考 `Sanhe.Vote.Base.Application.Admin.Rules.Providers`。 |
||||
|
||||
### 2. 注意规则中限制 IP 和地址 |
||||
|
||||
规则中限制 IP 和地址投票并没有进行过充分的测试,而且地址检测是通过 IP 来获取的,限制使用过调用第三方接口进行实现,充满了不确定性。 |
||||
|
||||
### 3. 选手列表缓存 |
||||
|
||||
现在选手列表缓存为了更好更好的完成业务,是将整个活动下选手一起缓存的。请注意选手参与人数与活动热度,如果这俩都非常大可能会造成内存不足的问题。 |
||||
|
||||
# 其他 |
||||
|
||||
[项目分支说明](http://git.sanhexinxi.com/guotianliang/svn-and-git-branch-description) |
||||
|
||||
[.NET Core 及 Abp vNext 框架记录](https://www.yuque.com/wwwk/dotnetcore/) |
||||
|
||||
[Abp vNext 自动化测试文档](https://docs.abp.io/zh-Hans/abp/latest/Testing) |
||||
|
||||
## 常见问题文档 |
||||
|
||||
- [ABP vNext 不使用工作单元为什么会抛出异常](https://www.cnblogs.com/myzony/p/11647030.html) |
||||
|
||||
- [Abp vNext 自定义 Ef Core 仓储引发异常](https://www.cnblogs.com/myzony/p/11863489.html) |
Loading…
Reference in new issue