Merge branch 'abp-dev' of https://gitee.com/ccnetcore/Yi into abp-dev

This commit is contained in:
橙子
2024-03-03 15:58:23 +08:00
17 changed files with 518 additions and 72 deletions

View File

@@ -44,6 +44,21 @@ namespace Yi.Framework.Bbs.Application.Services
}
}
/// <summary>
/// 获取全部访问流量
/// </summary>
/// <returns></returns>
public async Task<List<AccessLogDto>> Get()
{
var entities = await _repository._DbQueryable.OrderBy(x => x.CreationTime).ToListAsync();
var output = entities.Adapt<List<AccessLogDto>>();
output?.ForEach(x => x.CreationTime = x.CreationTime.Date);
return output;
}
/// <summary>
/// 触发
/// </summary>
@@ -106,5 +121,8 @@ namespace Yi.Framework.Bbs.Application.Services
return result.ToArray();
}
}
}

View File

@@ -93,5 +93,6 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
}
}
}
}

View File

@@ -2,6 +2,8 @@
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Application.Contracts.IServices;
using Yi.Framework.Bbs.Domain.Managers;
using Yi.Framework.Bbs.Domain.Shared.Etos;
namespace Yi.Framework.Bbs.Application.Services.Integral
@@ -9,7 +11,8 @@ namespace Yi.Framework.Bbs.Application.Services.Integral
public class LuckyService : ApplicationService
{
private ILocalEventBus _localEventBus;
public LuckyService(ILocalEventBus localEventBus) { _localEventBus = localEventBus; }
private BbsUserManager _bbsUserManager;
public LuckyService(ILocalEventBus localEventBus, BbsUserManager bbsUserManager) { _bbsUserManager = bbsUserManager; _localEventBus = localEventBus; }
/// <summary>
/// 大转盘
@@ -18,9 +21,22 @@ namespace Yi.Framework.Bbs.Application.Services.Integral
[Authorize]
public async Task<int> PostWheel()
{
int[] values=new int[10] { 0,10,30,50,60,80,90,100,200,666};
var userInfo = await _bbsUserManager.GetBbsUserInfoAsync(CurrentUser.Id.Value);
if (userInfo.Money < 50)
{
throw new UserFriendlyException("钱钱不足!");
}
int[] values = new int[10] { 0, 10, 30, 50, 60, 80, 90, 100, 200, 666 };
var index = GetWheelIndex();
var value = values[index]-50;
var value = values[index] - 50;
////不存在负数钱钱
//if (value < 0)
//{
// value = 0;
//}
//修改钱钱,如果钱钱不足,直接会丢出去,那本次抽奖将无效
await _localEventBus.PublishAsync(new MoneyChangeEventArgs { UserId = CurrentUser.Id!.Value, Number = value }, false);

View File

@@ -269,6 +269,7 @@ namespace Yi.Framework.Rbac.Application.Services
}
//此处优先从缓存中获取
UserRoleMenuDto output = null;
var cacheData = await _userCache.GetAsync(new UserInfoCacheKey(userId.Value));
if (cacheData is not null)
{

View File

@@ -18,7 +18,7 @@ namespace Yi.Framework.Rbac.Domain.Shared.Dtos
/// <summary>
/// 主键
/// </summary>
public Guid Id { get; protected set; }
public Guid Id { get; set; }
/// <summary>
/// 逻辑删除
@@ -134,7 +134,7 @@ namespace Yi.Framework.Rbac.Domain.Shared.Dtos
}
public class RoleDto
{
public Guid Id { get; protected set; }
public Guid Id { get; set; }
/// <summary>
/// 逻辑删除
@@ -194,7 +194,7 @@ namespace Yi.Framework.Rbac.Domain.Shared.Dtos
}
public class MenuDto
{
public Guid Id { get; protected set; }
public Guid Id { get; set; }
/// <summary>
/// 逻辑删除

View File

@@ -33,7 +33,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.Repositories
var user = await _DbQueryable.Includes(u => u.Roles.Where(r => r.IsDeleted == false).ToList(), r => r.Menus.Where(m => m.IsDeleted == false).ToList()).InSingleAsync(userId);
if (user is null)
{
throw new ArgumentNullException($"数错误用户id{nameof(userId)} 不存在");
throw new UserFriendlyException($"数错误用户id{nameof(userId)} 不存在,请重新登录");
}
user.Password = string.Empty;
user.Salt = string.Empty;

View File

@@ -15,3 +15,10 @@ export function getWeek() {
method: "get",
});
}
// 获取全部数据
export function getAccessList() {
return request({
url: "/access-log",
method: "get",
});
}

View File

@@ -0,0 +1,96 @@
/*
Atom One Dark by Daniel Gamage
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
base: #282c34
mono-1: #abb2bf
mono-2: #818896
mono-3: #5c6370
hue-1: #56b6c2
hue-2: #61aeee
hue-3: #c678dd
hue-4: #98c379
hue-5: #e06c75
hue-5-2: #be5046
hue-6: #d19a66
hue-6-2: #e6c07b
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #abb2bf;
background: #282c34;
}
.hljs-comment,
.hljs-quote {
color: #5c6370;
font-style: italic;
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #c678dd;
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e06c75;
}
.hljs-literal {
color: #56b6c2;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta-string {
color: #98c379;
}
.hljs-built_in,
.hljs-class .hljs-title {
color: #e6c07b;
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #d19a66;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #61aeee;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}

View File

@@ -1,3 +1,71 @@
<style scoped lang="scss">
/*::v-dedp .markdown-body pre{
padding: 0 !important;
}*/
::v-deep .pre-out
{
padding: 0;
overflow-x: hidden;
}
::v-deep .pre {
padding: 0;
margin-bottom: 0;
overflow-x: hidden;
.header {
background-color: #409eff;
color: white;
height: 30px;
display: flex;
justify-content: flex-end;
padding-top: 10px;
.language {}
.copy:hover {
cursor: pointer;
}
.copy {
margin: 0px 10px;
}
}
.code-con {
display: flex;
.nav {
display: block;
background-color: #282C34;
}
.code {
display: block;
padding: 10px 10px;
font-size: 14px;
line-height: 22px;
border-radius: 4px;
overflow-x: auto;
}
}
}
::v-deep .nav-ul {
border-right: 1px solid #FFFFFF;
margin-top: 12px;
padding-left: 10px;
padding-right: 2px;
.nav-li {
margin: 5.3px 0;
text-align: right;
margin-right: 3px;
}
}
</style>
<template>
<div>
<div class="markdown-body" v-html="outputHtml"></div>
@@ -9,24 +77,87 @@ import { marked } from 'marked';
import hljs from "highlight.js";
//可以设置加载样式切换主题
import 'highlight.js/styles/atom-one-dark.css'
import '@/assets/atom-one-dark.css'
import '@/assets/github-markdown.css'
import { ref,watch } from 'vue';
import {nextTick , ref, watch } from 'vue';
const BREAK_LINE_REGEXP = /\r\n|\r|\n/g;
const outputHtml=ref("")
const outputHtml = ref("")
const props = defineProps(['code'])
watch(props,(n,o)=>{
let codeCopyDic=[];
const addCopyEvent=()=>{
const copySpans = document.querySelectorAll('.copy');
console.log(copySpans,"copySpans");
// 为每个 copy span 元素添加点击事件
copySpans.forEach(span => {
span.addEventListener('click', async function() {
await navigator.clipboard.writeText(codeCopyDic.filter(x=>x.id==span.id)[0].code );
ElMessage({
message: "代码块复制成功",
type: "success",
duration: 2000,
});
});
});
}
//code部分处理、高亮
const codeHandler = (code, language) => {
const codeIndex = parseInt(Date.now() + "") + Math.floor(Math.random() * 10000000);
// 格式化第一行是右侧language和 “复制” 按钮;
if (code) {
const navCode = navHandler(code)
try {
// 使用 highlight.js 对代码进行高亮显示
const preCode = hljs.highlightAuto(code).value;
// 将代码包裹在 textarea 中由于防止textarea渲染出现问题这里将 "<" 用 "&lt;" 代替,不影响复制功能
let html = `<pre class='hljs pre'><div class="header"><span class="language">${language}</span><span class="copy" id="${codeIndex}">复制代码</span></div><div class="code-con"><div class="nav">${navCode}</div><code class="code">${preCode}</code></div></pre>`;
codeCopyDic.push({id: codeIndex,code:code});
console.log(codeCopyDic.length);
return html;
//<textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy${codeIndex}">${code.replace(/<\/textarea>/g, "&lt;/textarea>")}</textarea>
} catch (error) {
console.log(error);
}
}
}
//左侧导航栏处理
const navHandler = (code) => {
//获取行数
var linesCount = getLinesCount(code);
var currentLine = 1;
var liHtml = ``;
while (linesCount + 1 >= currentLine) {
liHtml += `<li class="nav-li">${currentLine}</li>`
currentLine++
}
let html = `<ul class="nav-ul">${liHtml}</ul>`
return html;
}
const getLinesCount = (text) => {
return (text.trim().match(BREAK_LINE_REGEXP) || []).length;
}
watch(props, (n, o) => {
codeCopyDic=[];
marked.setOptions({
renderer: new marked.Renderer(),
highlight: function(code) {
return hljs.highlightAuto(code).value;
},
highlight: function (code, language) {
return codeHandler(code, language);
// return hljs.highlightAuto(code).value;
},
pedantic: false,
gfm: true,//允许 Git Hub标准的markdown
tables: true,//支持表格
breaks: true,
sanitize: false,
smartLists: true,
@@ -36,8 +167,11 @@ watch(props,(n,o)=>{
}
);
//需要注意代码块样式
outputHtml.value = marked(n.code).replace(/<pre>/g, "<pre class='hljs'>")
},{immediate:true,deep:true})
const soureHtml = marked(n.code);
outputHtml.value= soureHtml.replace(/<pre>/g, '<pre class="pre-out">');
nextTick(()=>{
addCopyEvent();
})
}, { immediate: true, deep: true })
</script>

View File

@@ -2,7 +2,12 @@
import { ref } from "vue";
const props = defineProps(["items", "header", "text", "hideDivider", "height"]);
const emit = defineEmits(['onClickText'])
const height = ref(props.height + "px");
const onClickText=()=>{
emit('onClickText')
}
</script>
<template>
@@ -10,7 +15,7 @@ const height = ref(props.height + "px");
<template #header>
<div class="card-header">
<span>{{ props.header }}</span>
<el-link :underline="false" type="primary">{{ props.text }}</el-link>
<el-link :underline="false" type="primary" @click="onClickText">{{ props.text }}</el-link>
</div>
</template>

View File

@@ -6,22 +6,11 @@
<ScrollbarInfo />
</div>
<el-row class="left-div">
<el-col
:span="8"
v-for="i in plateList"
:key="i.id"
class="plate"
:style="{
'padding-left': i % 3 == 1 ? 0 : 0.2 + 'rem',
'padding-right': i % 3 == 0 ? 0 : 0.2 + 'rem',
}"
>
<PlateCard
:name="i.name"
:introduction="i.introduction"
:id="i.id"
:isPublish="i.isDisableCreateDiscuss"
/>
<el-col :span="8" v-for="i in plateList" :key="i.id" class="plate" :style="{
'padding-left': i % 3 == 1 ? 0 : 0.2 + 'rem',
'padding-right': i % 3 == 0 ? 0 : 0.2 + 'rem',
}">
<PlateCard :name="i.name" :introduction="i.introduction" :id="i.id" :isPublish="i.isDisableCreateDiscuss" />
</el-col>
<template v-if="isDiscussFinished">
<el-col :span="24" v-for="i in discussList" :key="i.id">
@@ -49,11 +38,7 @@
<div class="carousel-font" :style="{ color: item.color }">
{{ item.name }}
</div>
<el-image
style="width: 100%; height: 100%"
:src="item.logo"
fit="cover"
/>
<el-image style="width: 100%; height: 100%" :src="item.logo" fit="cover" />
</el-carousel-item>
</el-carousel>
</el-col>
@@ -101,20 +86,22 @@
</el-col>
<el-col :span="24">
<InfoCard header="访问统计" class="VisitsLineChart" text="详情">
<InfoCard header="访问统计" class="VisitsLineChart" text="(New)全站历史统计" @onClickText="onClickAccessLog">
<template #content>
<VisitsLineChart :option="statisOptions" class="statisChart" />
</template>
</InfoCard>
<el-dialog v-model="accessLogDialogVisible" title="全站历史统计" width="1200px" center>
<AccessLogChart :option="accessLogOptins" class="accessLogChart" />
</el-dialog>
</el-col>
<el-col :span="24">
<InfoCard header="简介" text="详情">
<InfoCard header="简介" text="">
<template #content>
<div class="introduce">
没有什么能够阻挡人类对代码<span style="color: #1890ff"
>优雅</span
>的追求
没有什么能够阻挡人类对代码<span style="color: #1890ff">优雅</span>的追求
</div>
</template>
</InfoCard>
@@ -122,12 +109,7 @@
<el-col :span="24">
<template v-if="isPointFinished">
<InfoCard
:items="pointList"
header="财富排行榜"
text="关于钱钱"
height="400"
>
<InfoCard :items="pointList" header="财富排行榜" text="关于钱钱" height="400">
<template #item="temp">
<PointsRanking :pointsData="temp" />
</template>
@@ -135,19 +117,16 @@
</template>
<template v-else>
<InfoCard header="本月排行" text="更多">
<template #content> <Skeleton /></template>
<template #content>
<Skeleton />
</template>
</InfoCard>
</template>
</el-col>
<el-col :span="24">
<template v-if="isFriendFinished">
<InfoCard
:items="friendList"
header="推荐好友"
text="更多"
height="400"
>
<InfoCard :items="friendList" header="推荐好友" text="更多" height="400">
<template #item="temp">
<RecommendFriend :friendData="temp" />
</template>
@@ -155,18 +134,15 @@
</template>
<template v-else>
<InfoCard header="推荐好友" text="更多">
<template #content> <Skeleton /></template>
<template #content>
<Skeleton />
</template>
</InfoCard>
</template>
</el-col>
<el-col :span="24">
<template v-if="isThemeFinished">
<InfoCard
:items="themeList"
header="推荐主题"
text="更多"
height="400"
>
<InfoCard :items="themeList" header="推荐主题" text="更多" height="400">
<template #item="temp">
<ThemeData :themeData="temp" />
</template>
@@ -174,7 +150,9 @@
</template>
<template v-else>
<InfoCard header="推荐主题" text="更多">
<template #content> <Skeleton /></template>
<template #content>
<Skeleton />
</template>
</InfoCard>
</template>
</el-col>
@@ -197,7 +175,8 @@ import PlateCard from "@/components/PlateCard.vue";
import ScrollbarInfo from "@/components/ScrollbarInfo.vue";
import BottomInfo from "@/components/BottomInfo.vue";
import VisitsLineChart from "./components/VisitsLineChart/index.vue";
import { access } from "@/apis/accessApi.js";
import AccessLogChart from "./components/AccessLogChart/Index.vue"
import { access, getAccessList } from "@/apis/accessApi.js";
import { getList } from "@/apis/plateApi.js";
import { getList as bannerGetList } from "@/apis/bannerApi.js";
import { getHomeDiscuss } from "@/apis/discussApi.js";
@@ -215,10 +194,10 @@ import ThemeData from "./components/RecommendTheme/index.vue";
import Skeleton from "@/components/Skeleton/index.vue";
import useSocketStore from "@/stores/socket";
const accessLogDialogVisible = ref(false)
const router = useRouter();
const accessAllList = ref([]);
const plateList = ref([]);
const discussList = ref([]);
@@ -294,6 +273,25 @@ const statisOptions = computed(() => {
},
};
});
//历史全部访问统计
const accessLogOptins = computed(() => {
return {
xAxis: {
data: accessAllList.value?.map((item,index)=>{
return item.creationTime.slice(0, 10);
})
},
series: [
{
data: accessAllList.value?.map((item,index)=>{
return item.number;
})
}
]
}
});
const handleToSign = () => {
router.push("/activity/sign");
@@ -309,12 +307,19 @@ watch(
{ deep: true }
);
const onClickAccessLog = async () => {
accessLogDialogVisible.value = true;
const {data} = await getAccessList();
accessAllList.value = data;
}
</script>
<style scoped lang="scss">
.home-box {
width: 1300px;
height: 100%;
.introduce {
color: rgba(0, 0, 0, 0.45);
font-size: small;
@@ -350,6 +355,7 @@ watch(
display: block;
margin-bottom: 0.5rem;
}
.analyse {
display: flex;
justify-content: space-between;
@@ -357,6 +363,7 @@ watch(
width: 100%;
height: 100px;
margin-bottom: 10px;
.item {
width: 30%;
height: 100%;
@@ -368,6 +375,7 @@ watch(
border: 1px solid #409eff;
border-radius: 5px;
color: #409eff;
.content {
width: 100%;
height: 100%;
@@ -385,6 +393,7 @@ watch(
.name {
font-size: 14px;
}
.count {
font-size: 20px;
font-weight: bold;
@@ -407,11 +416,13 @@ watch(
}
}
}
.signIn {
display: flex;
justify-content: space-between;
align-items: center;
color: #8a919f;
&-btn {
cursor: pointer;
display: flex;
@@ -426,7 +437,7 @@ watch(
}
}
.VisitsLineChart >>> .el-card__body {
.VisitsLineChart>>>.el-card__body {
padding: 0.5rem;
}
@@ -434,5 +445,10 @@ watch(
width: 100%;
height: 300px;
}
.accessLogChart {
width: 1100px;
height: 500px;
}
}
</style>

View File

@@ -0,0 +1,31 @@
<template>
<div class="v-chart" ref="statis"></div>
</template>
<script setup name="AccessLogChart">
import { ref, defineEmits, defineProps, defineExpose } from "vue";
import useEcharts from "@/hooks/useEcharts";
import { accessLogEchartsConfig } from "../../hooks/accessLogEchartsConfig";
const props = defineProps({
option: {
type: Object,
default: () => {},
},
});
const emits = defineEmits([
"chart-click", // 点击chart
]);
let statis = ref(null);
const { resize } = useEcharts(statis, emits, props, accessLogEchartsConfig);
defineExpose({
resize,
});
</script>
<style scoped lang="scss">
.v-chart {
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,18 @@
export const accessLogEchartsConfig = {
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value'
},
series: [
{
data: [],
type: 'line',
areaStyle: {}
}
]
};

View File

@@ -79,8 +79,22 @@ const startCallback = () => {
// 假设后端返回的中奖索引是0
// 调用stop停止旋转并传递中奖索引
}, 3000)
try
{
const ddd=(await luckyWheel()).data;
console.log(ddd,"dd");
index= (await luckyWheel()).data;
myLucky.value.stop(index)
}
catch
{
}
finally{
myLucky.value.stop(index)
}
})

View File

@@ -0,0 +1,89 @@
大家好呀~ 最近我这里收到很多人的疑惑
- 从零开始我怎么手动分层依赖搭建一个项目
- 如果想依赖其他组件应该怎么引入
- 为什么要这样分层?和三层架构有什么区别?
- 等等
这些问题,很多初学者会疑惑的地方,就是照葫芦画苗,但是也说不出理由来,最后反而失去了这样分层的优势。
每一层都是有它的理由,明确了意义,在编码阶段才更得心应手,发挥出这样分层的作用。
> 题外话很是有缘在完全没有接触过DDD的分层的时候还是停留在三层架构的时期对于大型复杂的依赖关系Service满天飞根本难以下手写出的代码交错复杂稍微隔一段时间没去回顾再去看就会被自己的代码给绕死所以我就一直想换一种分层模式想让各个服务结构更加清晰不再是直肠子式的三层架构那我做的第一件事情就是把关系比较紧密的给装在一起当成一个整体各个整体之间不轻易去依赖而是通过事件去抛出去。这就在早期形成了一套依赖关系比较清晰的框架经过不断的项目历练慢慢的就变成了业务聚合、应用组装等等概念等到机会接触到了DDD分层一看`哇塞,怎么那么像?这怕不是他抄我的分层嘛`,当然开个玩笑,从此对这块的了解更加加深,确实值得很多大家应该学习的地方,提升一下自己。
> 一千个码农就有一千个DDD。DDD不一定完全是对的但一定是值得学习使用的
> 在此,老橙子很乐意以我的血淋淋的教训的角度给大家讲清楚,框架的分层依赖关系,同时也是标准的`DDD分层架构`依赖关系,希望能给大家带来一些帮助~
## 依赖关系
这个标题其实已经在之前一个章节有涉及到一部分,`目录结构`,不过既然想弄清楚依赖关系,就得先了解目录结构,大家可以先回顾一下,弄清楚有哪些文件,再看它的依赖关系
![alt text](image-3.png)
从上至下,分别是
- 应用层
- 应用抽象层
- 领域层
- 领域共享层
- 基础设施ORM层
- Host主机
对应的项目引用关系下图所示:
![alt text](image-2.png)
可以从上图很直观的看出:
最顶层的`host主机与下面的应用`没有关系意味着host主机可以是web、可以是cs、可以等等所以我们的host主机只充当一个web启动的作用不包含业务逻辑
另外发现目录中只包含ORM好像没有基础设施层其实是一个包含关系是ORM属于基础设施中的一种但是为了将orm抽象特意把ORM独立了一层
由于框架的基础设施都已经在Framework里面包含所以没有单独建立一层新的基础设施层你完全可以`再加一层`在ORM下当作基础设施即可。
> web应用中对数据库操作涉及非常广所以ABP将ORM特意抽象出来可实现EFCore、MongoDb等数据库无缝切换。
## 每层的内容
- **基础设施层**存放各种通用方法、ORM等
- **领域共享层**存放枚举、常量、事件参数、对应用层的dto
- **领域层**存放业务的内部实现领域服务、领域事件。对外也就是对应用层可以暴露实体、对应用层的dto
- **应用抽象层**存放应用层抽象、系统对外的dto
- **应用层**存放对业务的组装领域服务对外暴露的dto主要是对外的用户交互相关的业务
- **Host主机**存放跟WebHost类似web启动器包括中间件、swagger、等与web相关
## 为什么要这样分层?
弄清楚了分层结构,已经依赖关系,我们可以探究里面的原理,我们为什么要这样做?这样做的道理或者好处是什么?
### 基础设施层
首先,不管是任何系统,想要对通用方法的复用,就必然有基础设施供整个系统的使用。这个`基础设施层`其实就类似于三层架构的`Common层`,这个没啥说的
这一层目的是为了通用方法的复用
### 领域层
领域,这个词经常出现,感觉很抽象,我们可以把他类比成`盒子`或者`模块`,比如电商系统的售前、售后,我们这可以分成两个大的`领域`
这里是整个结构的业务实现在这块,也是最重的一层。严格来说,不同领域之间想要调用,比如售后领域想查看售前领域的记录,可以通过事件抛出来,从而各个领域没有之间强耦合。
这一层的作用是实现各个领域的业务
### 领域共享层
这里只是存放一些领域所要使用的枚举、常量等,除了基础设施不要有其他的依赖,轻、也轻越好,因为他的目的不是对内,而是提供给给外部系统使用的。
这一层的作用是存放共享的枚举、常量等也是为了存放领域层暴露给应用层的dto比如领域层想暴露一个用户角色菜单对象实体无法满足就需可以创建这样的一个dto了在其他文章可能大家会叫做info对象
### 应用层
领域层是对内的外部系统是不清楚的那我们的业务实现想暴露出去要通过应用层进行包装因为应用层是对外的对用户交互相关处理比如api、signalr等
这一层的目的是对领域层的组装及数据的效验、对外用户交互相关处理。
### 应用抽象层
这里主要目的是将应用层暴露进行抽象,该层是为了提供外部系统之间依赖,于`领域共享层`类似,所以这一层也非常的轻,从上面的依赖关系图来看,`领域共享层``应用抽象层`是一条线性依赖。
这一层目的是为了`应用抽象层`可以被外部系统之间引用从而通过Http代理等技术实现远程调用接口访问`应用层`进行交互。这里感兴趣的可以看一下Abp.VNext的HttpProxy模块默认意框架中不引入等有需要的时候可以使用。
## 小结
以上分层是DDD推荐的一种最佳实践方式之一很荣幸通过我十几个上线项目应用情况事实证明非常nice
补充一句话我不是DDD的崇拜者只是在被各种业务折磨的不行的码农想追求一种方法让花更少的时间更好的去完成任务。
所以,我的内容是比较偏向实践,正如意框架一般,一个追求以用户体验为中心的框架。
意框架很多地方并没有完全严格走DDD包括Abp等有名的框架过于严格代码体验感反而是下降。有很多场景其实是没有必要的过长的代码、过绕的领域关系写个增删改查还得花很多时间这也是为什么DDD难以被接受的原因之一。

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB