add 20250126-1558

This commit is contained in:
ditiqi 2025-01-26 15:28:34 +08:00
parent 11a1a7610b
commit e8b23533d7
9 changed files with 264 additions and 208 deletions

View File

@ -169,12 +169,16 @@ export class AuthService {
password,
officerId,
showname,
department: {
department: deptId
? {
connect: { id: deptId },
},
domain: {
}
: undefined,
domain: deptId
? {
connect: { id: deptId },
},
}
: undefined,
// domainId: data.deptId,
meta: {
photoUrl,

View File

@ -31,7 +31,6 @@ export async function setPostRelation(params: {
visitorId: staff?.id,
},
})) > 0;
const liked =
(await db.visit.count({
where: {
@ -50,6 +49,24 @@ export async function setPostRelation(params: {
}),
},
})) > 0;
const hated =
(await db.visit.count({
where: {
postId: data.id,
type: VisitType?.HATE,
...(staff?.id
? // 如果有 staff查找对应的 visitorId
{ visitorId: staff.id }
: // 如果没有 staff查找相同 IP 且 visitorId 为 null 且 30 分钟内的记录
{
visitorId: null,
meta: { path: ['ip'], equals: clientIp },
updatedAt: {
gte: thirtyMinutesAgo,
},
}),
},
})) > 0;
const readedCount = await db.visit.count({
where: {
postId: data.id,
@ -61,6 +78,7 @@ export async function setPostRelation(params: {
readed,
readedCount,
liked,
hated,
commentsCount,
});
// console.log('data', data);

View File

@ -78,6 +78,13 @@ export class VisitService extends BaseService<Prisma.VisitDelegate> {
visitType: VisitType.LIKE,
});
}
if (args.data.type === VisitType.HATE) {
EventBus.emit('updateVisitCount', {
objectType: ObjectType.POST,
id: postId,
visitType: VisitType.HATE,
});
}
}
return result;
}

View File

@ -37,19 +37,20 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
layout="vertical"
onFinish={onSubmit}
scrollToFirstError>
<div className=" flex items-center gap-4 mb-2">
<div>
<div className="flex items-center gap-2 mb-2">
<div className="flex-1">
<Form.Item name="photoUrl" label="头像" noStyle>
<AvatarUploader
className="rounded-lg"
placeholder="点击上传头像"
style={{
height: 150,
width: 120,
}}></AvatarUploader>
width: `100%`,
height: 210,
}}
/>
</Form.Item>
</div>
<div className="grid grid-cols-1 gap-2 flex-1">
<div className="flex-1 grid grid-cols-1 gap-3">
<Form.Item
name="username"
label="用户名"
@ -88,12 +89,8 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
noStyle
label="部门"
rules={[{ required: true, message: "请选择部门" }]}>
<DepartmentSelect
rootId={domainId}></DepartmentSelect>
<DepartmentSelect rootId={domainId} />
</Form.Item>
</div>
</div>
<div className="grid grid-cols-1 gap-2 flex-1 mb-2">
<Form.Item noStyle name={"rank"}>
<Input
placeholder="请输入职级(可选)"
@ -102,19 +99,10 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
allowClear
/>
</Form.Item>
<Form.Item
name="officerId"
label="证件号"
noStyle
rules={[
{ required: true, message: "请输入证件号" },
{
pattern: /^\d{5,12}$/,
message: "请输入有效的证件号5-12位数字",
},
]}>
<Input placeholder="证件号(可选)" />
</Form.Item>
</div>
</div>
<div className="flex items-center gap-2">
<div className="flex-1 grid grid-cols-1 gap-2">
<Form.Item
noStyle
rules={[
@ -133,17 +121,9 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
placeholder="请输入手机号(可选)"
/>
</Form.Item>
<Form.Item noStyle name={"email"}>
<Input
placeholder="请输入邮箱(可选)"
autoComplete="off"
spellCheck={false}
allowClear
/>
</Form.Item>
<Form.Item noStyle name={"office"}>
<Input
placeholder="请输入办公室地点(可选)"
placeholder="请输入办公室地点"
autoComplete="off"
spellCheck={false}
allowClear
@ -151,8 +131,6 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
</Form.Item>
<Form.Item
name="password"
label="密码"
noStyle
rules={[
{ required: true, message: "请输入密码" },
{ min: 8, message: "密码至少需要8个字符" },
@ -165,11 +143,31 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
]}>
<Input.Password placeholder="密码" />
</Form.Item>
</div>
<div className="flex-1 grid grid-cols-1 gap-2">
<Form.Item
name="officerId"
label="证件号"
noStyle
rules={[
{ required: true, message: "请输入证件号" },
{
pattern: /^\d{5,12}$/,
message: "请输入有效的证件号5-12位数字",
},
]}>
<Input placeholder="证件号(可选)" />
</Form.Item>
<Form.Item noStyle name={"email"}>
<Input
placeholder="请输入邮箱(可选)"
autoComplete="off"
spellCheck={false}
allowClear
/>
</Form.Item>
<Form.Item
name="repeatPass"
label="确认密码"
noStyle
dependencies={["password"]}
rules={[
{ required: true, message: "请确认密码" },
@ -190,6 +188,9 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
<Input.Password placeholder="确认密码" />
</Form.Item>
</div>
</div>
<div className="grid grid-cols-1 flex-1 my-2"></div>
<Form.Item>
<Button
type="primary"

View File

@ -24,13 +24,12 @@ export function LetterCard({ letter }: LetterCardProps) {
return (
<div
onClick={() => {
window.open(`/${letter.id}/detail`)
window.open(`/${letter.id}/detail`);
}}
className="cursor-pointer p-6 bg-slate-100/80 rounded-xl hover:ring-white hover:ring-1 transition-all
duration-300 ease-in-out hover:-translate-y-0.5
active:scale-[0.98] border border-white
group relative overflow-hidden"
>
group relative overflow-hidden">
<div className="flex flex-col gap-4">
<div className=" text-xl text-primary font-bold">
{letter.title}
@ -49,21 +48,27 @@ export function LetterCard({ letter }: LetterCardProps) {
<div className="flex items-center gap-2">
<UserOutlined className="text-primary text-base" />
<Text className="text-primary font-medium">
{letter.author?.showname || '匿名用户'}
{letter.author?.showname || "匿名用户"}
</Text>
</div>
{letter.receivers.some(item => item.showname) && (
{letter.receivers.some((item) => item.showname) && (
<div className="flex items-center gap-2">
<MailOutlined className="text-primary-400 text-base" />
<Tooltip title={letter.receivers.map(item => item.showname).filter(Boolean).join(', ')}>
<Tooltip
title={letter?.receivers
?.map((item) => item.showname)
.filter(Boolean)
.join(", ")}>
<Text className="text-primary-400">
{letter.receivers
.map(item => item.showname)
.map((item) => item.showname)
.filter(Boolean)
.slice(0, 2)
.join('、')}
{letter.receivers.filter(item => item.showname).length > 2 && ' 等'}
.join("、")}
{letter.receivers.filter(
(item) => item.showname
).length > 2 && " 等"}
</Text>
</Tooltip>
</div>
@ -91,11 +96,15 @@ export function LetterCard({ letter }: LetterCardProps) {
<div className="flex justify-between items-center ">
<div className="flex flex-wrap gap-2">
<LetterBadge type="state" value={letter.state} />
{letter.meta.tags.map(tag => (
{letter?.meta?.tags?.map((tag) => (
<LetterBadge key={tag} type="tag" value={tag} />
))}
{letter.terms.map(term => (
<LetterBadge key={term.name} type="category" value={term.name} />
{letter.terms.map((term) => (
<LetterBadge
key={term.name}
type="category"
value={term.name}
/>
))}
</div>
@ -103,9 +112,9 @@ export function LetterCard({ letter }: LetterCardProps) {
<Button
type="default"
shape="round"
icon={<EyeOutlined />}
>
<span className="mr-1"></span>{letter.views}
icon={<EyeOutlined />}>
<span className="mr-1"></span>
{letter.views}
</Button>
<PostLikeButton post={letter as any}></PostLikeButton>
</div>

View File

@ -118,6 +118,21 @@ export function useVisitor() {
}))
);
const hate = api.visitor.create.useMutation(
createOptimisticMutation((item) => ({
...item,
hates: (item.hates || 0) + 1,
hated: true,
}))
);
const unHate = api.visitor.deleteMany.useMutation(
createOptimisticMutation((item) => ({
...item,
hates: item.hates - 1 || 0,
hated: false,
}))
);
const addStar = api.visitor.create.useMutation(
createOptimisticMutation((item) => ({
...item,

View File

@ -206,6 +206,7 @@ model Post {
visits Visit[] // 访问记录,关联 Visit 模型
views Int @default(0)
likes Int @default(0)
hates Int @default(0)
receivers Staff[] @relation("post_receiver")
parentId String? @map("parent_id")
parent Post? @relation("PostChildren", fields: [parentId], references: [id]) // 父级帖子,关联 Post 模型

View File

@ -14,6 +14,7 @@ export enum VisitType {
STAR = "star",
READED = "read",
LIKE = "like",
HATE = "hate",
}
export enum StorageProvider {