oss接通了80端口,还没有接通https,先用
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
export type StoreCreateForm = {
|
||||||
|
partnerId: string;
|
||||||
|
cityId: string;
|
||||||
|
name: string;
|
||||||
|
phone: string;
|
||||||
|
district: string;
|
||||||
|
address: string;
|
||||||
|
intro?: string;
|
||||||
|
coverUrl?: string;
|
||||||
|
envPhotoUrls?: string[];
|
||||||
|
contractUrl?: string;
|
||||||
|
bankAccountName: string;
|
||||||
|
bankAccountNo: string;
|
||||||
|
bankBranch: string;
|
||||||
|
accountPhone?: string;
|
||||||
|
accountName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PHONE_RE = /^1\d{10}$/;
|
||||||
|
const BANK_RE = /^\d{16,19}$/;
|
||||||
|
|
||||||
|
export function validateStoreCreateStep1(form: Pick<StoreCreateForm, 'partnerId' | 'cityId' | 'name' | 'phone' | 'district' | 'address' | 'intro'>): string | null {
|
||||||
|
if (!form.partnerId) return '请选择开城合伙人';
|
||||||
|
if (!form.cityId) return '请选择开城城市';
|
||||||
|
if (!form.name?.trim()) return '请填写门店名称';
|
||||||
|
if (!form.phone?.trim()) return '请填写联系电话';
|
||||||
|
if (!PHONE_RE.test(form.phone.trim())) return '联系电话须为11位手机号';
|
||||||
|
if (!form.district?.trim()) return '请填写区县';
|
||||||
|
if (!form.address?.trim()) return '请填写详细地址';
|
||||||
|
if (form.intro?.trim()) {
|
||||||
|
const len = form.intro.trim().length;
|
||||||
|
if (len < 10 || len > 500) return '门店简介须为 10~500 字';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateStoreCreateStep3(form: Pick<StoreCreateForm, 'bankAccountName' | 'bankAccountNo' | 'bankBranch'>): string | null {
|
||||||
|
if (!form.bankAccountName?.trim()) return '请填写户主姓名';
|
||||||
|
if (!form.bankAccountNo?.trim()) return '请填写银行卡号';
|
||||||
|
if (!BANK_RE.test(form.bankAccountNo.replace(/\s/g, ''))) return '银行卡号须为 16~19 位数字';
|
||||||
|
if (!form.bankBranch?.trim()) return '请填写开户支行';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,13 +1,38 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Button, Descriptions, Drawer, Form, Image, Input, Modal, Select, Space, Table, Tag, Typography, message,
|
Alert,
|
||||||
|
Button,
|
||||||
|
Descriptions,
|
||||||
|
Drawer,
|
||||||
|
Form,
|
||||||
|
Image,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Steps,
|
||||||
|
Table,
|
||||||
|
Tag,
|
||||||
|
Typography,
|
||||||
|
message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
import { request, type Paginated } from '../lib/api';
|
import { request, type Paginated } from '../lib/api';
|
||||||
import { STORE_STATUS_LABELS, fmtTime } from '../lib/constants';
|
import { STORE_STATUS_LABELS, fmtTime } from '../lib/constants';
|
||||||
|
import {
|
||||||
|
validateStoreCreateStep1,
|
||||||
|
validateStoreCreateStep3,
|
||||||
|
type StoreCreateForm,
|
||||||
|
} from '../lib/storeCreate';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import OssUpload from '../components/OssUpload';
|
import OssUpload from '../components/OssUpload';
|
||||||
|
|
||||||
|
const CREATE_STEPS = [
|
||||||
|
{ title: '基本信息' },
|
||||||
|
{ title: '照片上传' },
|
||||||
|
{ title: '结算资质' },
|
||||||
|
];
|
||||||
|
|
||||||
type StoreRow = {
|
type StoreRow = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -25,12 +50,12 @@ type StoreRow = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type PartnerOption = { id: string; companyName: string };
|
type PartnerOption = { id: string; companyName: string };
|
||||||
type CityOption = { id: string; name: string; code: string };
|
type CityOption = { id: string; name: string; code: string; partnerId?: string | null };
|
||||||
|
|
||||||
export default function StoresPage() {
|
export default function StoresPage() {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [editForm] = Form.useForm();
|
const [editForm] = Form.useForm();
|
||||||
const [createForm] = Form.useForm();
|
const [createForm] = Form.useForm<StoreCreateForm>();
|
||||||
const [filters, setFilters] = useState<Record<string, string>>({});
|
const [filters, setFilters] = useState<Record<string, string>>({});
|
||||||
const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<StoreRow>(
|
const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<StoreRow>(
|
||||||
'/admin/stores',
|
'/admin/stores',
|
||||||
@@ -46,9 +71,13 @@ export default function StoresPage() {
|
|||||||
const [detail, setDetail] = useState<Record<string, unknown> | null>(null);
|
const [detail, setDetail] = useState<Record<string, unknown> | null>(null);
|
||||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||||
const [createOpen, setCreateOpen] = useState(false);
|
const [createOpen, setCreateOpen] = useState(false);
|
||||||
|
const [createStep, setCreateStep] = useState(0);
|
||||||
|
const [createError, setCreateError] = useState('');
|
||||||
const [partners, setPartners] = useState<PartnerOption[]>([]);
|
const [partners, setPartners] = useState<PartnerOption[]>([]);
|
||||||
const [cities, setCities] = useState<CityOption[]>([]);
|
const [cities, setCities] = useState<CityOption[]>([]);
|
||||||
|
|
||||||
|
const selectedPartnerId = Form.useWatch('partnerId', createForm);
|
||||||
|
|
||||||
async function loadOptions() {
|
async function loadOptions() {
|
||||||
const [p, c] = await Promise.all([
|
const [p, c] = await Promise.all([
|
||||||
request<Paginated<PartnerOption>>('/admin/partners?pageSize=200'),
|
request<Paginated<PartnerOption>>('/admin/partners?pageSize=200'),
|
||||||
@@ -58,6 +87,85 @@ export default function StoresPage() {
|
|||||||
setCities(c.items);
|
setCities(c.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeCreateModal() {
|
||||||
|
setCreateOpen(false);
|
||||||
|
setCreateStep(0);
|
||||||
|
setCreateError('');
|
||||||
|
createForm.resetFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreateModal() {
|
||||||
|
void loadOptions();
|
||||||
|
createForm.setFieldsValue({
|
||||||
|
envPhotoUrls: ['', '', ''],
|
||||||
|
});
|
||||||
|
setCreateStep(0);
|
||||||
|
setCreateError('');
|
||||||
|
setCreateOpen(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCreateNext() {
|
||||||
|
const values = createForm.getFieldsValue();
|
||||||
|
if (createStep === 0) {
|
||||||
|
const msg = validateStoreCreateStep1(values);
|
||||||
|
if (msg) {
|
||||||
|
setCreateError(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await createForm.validateFields(['partnerId', 'cityId', 'name', 'phone', 'district', 'address']);
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCreateError('');
|
||||||
|
setCreateStep((s) => s + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCreateSubmit() {
|
||||||
|
const values = createForm.getFieldsValue();
|
||||||
|
const step1Msg = validateStoreCreateStep1(values);
|
||||||
|
if (step1Msg) {
|
||||||
|
setCreateError(step1Msg);
|
||||||
|
setCreateStep(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const step3Msg = validateStoreCreateStep3(values);
|
||||||
|
if (step3Msg) {
|
||||||
|
setCreateError(step3Msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const envPhotoUrls = (values.envPhotoUrls ?? []).map((u) => u?.trim()).filter(Boolean) as string[];
|
||||||
|
await request('/admin/stores', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
partnerId: values.partnerId,
|
||||||
|
cityId: values.cityId,
|
||||||
|
name: values.name.trim(),
|
||||||
|
phone: values.phone.trim(),
|
||||||
|
district: values.district.trim(),
|
||||||
|
address: values.address.trim(),
|
||||||
|
intro: values.intro?.trim() || undefined,
|
||||||
|
coverUrl: values.coverUrl?.trim() || undefined,
|
||||||
|
envPhotoUrls: envPhotoUrls.length ? envPhotoUrls : undefined,
|
||||||
|
contractUrl: values.contractUrl?.trim() || undefined,
|
||||||
|
bankAccountName: values.bankAccountName.trim(),
|
||||||
|
bankAccountNo: values.bankAccountNo.replace(/\s/g, ''),
|
||||||
|
bankBranch: values.bankBranch.trim(),
|
||||||
|
accountPhone: values.accountPhone?.trim() || undefined,
|
||||||
|
accountName: values.accountName?.trim() || undefined,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
message.success('门店已创建');
|
||||||
|
closeCreateModal();
|
||||||
|
void reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredCities = selectedPartnerId
|
||||||
|
? cities.filter((c) => String(c.partnerId ?? '') === selectedPartnerId)
|
||||||
|
: cities;
|
||||||
|
|
||||||
const columns: ColumnsType<StoreRow> = [
|
const columns: ColumnsType<StoreRow> = [
|
||||||
{
|
{
|
||||||
title: '封面', dataIndex: 'coverUrl', width: 72,
|
title: '封面', dataIndex: 'coverUrl', width: 72,
|
||||||
@@ -91,7 +199,7 @@ export default function StoresPage() {
|
|||||||
<div>
|
<div>
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>门店</Typography.Title>
|
<Typography.Title level={4} style={{ margin: 0 }}>门店</Typography.Title>
|
||||||
<Button type="primary" onClick={() => { void loadOptions(); setCreateOpen(true); }}>新建门店</Button>
|
<Button type="primary" onClick={openCreateModal}>新建门店</Button>
|
||||||
</Space>
|
</Space>
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||||
<Form.Item name="name" label="名称"><Input allowClear /></Form.Item>
|
<Form.Item name="name" label="名称"><Input allowClear /></Form.Item>
|
||||||
@@ -148,31 +256,111 @@ export default function StoresPage() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
<Modal title="新建门店" open={createOpen} width={560} onCancel={() => setCreateOpen(false)} onOk={async () => {
|
<Modal
|
||||||
const v = await createForm.validateFields();
|
title="新建门店"
|
||||||
await request('/admin/stores', { method: 'POST', body: JSON.stringify(v) });
|
open={createOpen}
|
||||||
message.success('已创建');
|
width={640}
|
||||||
setCreateOpen(false);
|
onCancel={closeCreateModal}
|
||||||
createForm.resetFields();
|
destroyOnClose
|
||||||
void reload();
|
footer={(
|
||||||
}}>
|
<Space>
|
||||||
|
{createStep > 0 && <Button onClick={() => { setCreateError(''); setCreateStep((s) => s - 1); }}>上一步</Button>}
|
||||||
|
{createStep < 2 ? (
|
||||||
|
<Button type="primary" onClick={() => void handleCreateNext()}>下一步</Button>
|
||||||
|
) : (
|
||||||
|
<Button type="primary" onClick={() => void handleCreateSubmit()}>提交</Button>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Steps current={createStep} items={CREATE_STEPS} style={{ marginBottom: 24 }} />
|
||||||
|
{createError && (
|
||||||
|
<Alert type="error" message={createError} showIcon style={{ marginBottom: 16 }} />
|
||||||
|
)}
|
||||||
<Form form={createForm} layout="vertical">
|
<Form form={createForm} layout="vertical">
|
||||||
<Form.Item name="partnerId" label="开城合伙人" rules={[{ required: true }]}>
|
{createStep === 0 && (
|
||||||
<Select showSearch optionFilterProp="label" options={partners.map((p) => ({ value: p.id, label: p.companyName }))} />
|
<>
|
||||||
</Form.Item>
|
<Form.Item name="partnerId" label="开城合伙人" rules={[{ required: true, message: '请选择开城合伙人' }]}>
|
||||||
<Form.Item name="cityId" label="开城城市" rules={[{ required: true }]}>
|
<Select
|
||||||
<Select showSearch optionFilterProp="label" options={cities.map((c) => ({ value: c.id, label: `${c.name} (${c.code})` }))} />
|
showSearch
|
||||||
</Form.Item>
|
optionFilterProp="label"
|
||||||
<Form.Item name="name" label="门店名" rules={[{ required: true }]}><Input /></Form.Item>
|
options={partners.map((p) => ({ value: p.id, label: p.companyName }))}
|
||||||
<Form.Item name="phone" label="门店电话" rules={[{ required: true }]}><Input /></Form.Item>
|
onChange={() => createForm.setFieldValue('cityId', undefined)}
|
||||||
<Form.Item name="address" label="详细地址" rules={[{ required: true }]}><Input /></Form.Item>
|
/>
|
||||||
<Form.Item name="district" label="区县"><Input /></Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="coverUrl" label="封面图">
|
<Form.Item name="cityId" label="开城城市" rules={[{ required: true, message: '请选择开城城市' }]}>
|
||||||
<OssUpload bizType="COVER" mediaType="IMAGE" />
|
<Select
|
||||||
</Form.Item>
|
showSearch
|
||||||
<Form.Item name="intro" label="介绍"><Input.TextArea rows={3} /></Form.Item>
|
optionFilterProp="label"
|
||||||
<Form.Item name="accountPhone" label="店长手机"><Input placeholder="默认同门店电话" /></Form.Item>
|
options={filteredCities.map((c) => ({ value: c.id, label: `${c.name} (${c.code})` }))}
|
||||||
<Form.Item name="accountName" label="店长姓名"><Input placeholder="默认同门店名" /></Form.Item>
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="name" label="门店名称" rules={[{ required: true, message: '请填写门店名称' }]}>
|
||||||
|
<Input placeholder="请输入门店名称" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="phone" label="联系电话" rules={[{ required: true, message: '请填写联系电话' }]}>
|
||||||
|
<Input placeholder="11位手机号" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="district" label="区县" rules={[{ required: true, message: '请填写区县' }]}>
|
||||||
|
<Input placeholder="例如:金水区" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="address" label="详细地址" rules={[{ required: true, message: '请填写详细地址' }]}>
|
||||||
|
<Input.TextArea rows={2} placeholder="请输入详细门牌号" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="intro" label="门店简介">
|
||||||
|
<Input.TextArea rows={3} placeholder="选填,10~500字" showCount maxLength={500} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="accountPhone" label="店长手机">
|
||||||
|
<Input placeholder="默认同门店电话" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="accountName" label="店长姓名">
|
||||||
|
<Input placeholder="默认同门店名" />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{createStep === 1 && (
|
||||||
|
<>
|
||||||
|
<Typography.Paragraph type="secondary">
|
||||||
|
preV1 照片上传为选填,可直接下一步(与合伙人端一致)。
|
||||||
|
</Typography.Paragraph>
|
||||||
|
<Form.Item name="coverUrl" label="门头照">
|
||||||
|
<OssUpload bizType="COVER" mediaType="IMAGE" />
|
||||||
|
</Form.Item>
|
||||||
|
<Typography.Text strong>环境照片</Typography.Text>
|
||||||
|
<Form.List name="envPhotoUrls">
|
||||||
|
{(fields) => (
|
||||||
|
<div style={{ marginTop: 8 }}>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key} name={field.name} label={`环境图 ${index + 1}`}>
|
||||||
|
<OssUpload bizType="ENV" mediaType="IMAGE" />
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Form.List>
|
||||||
|
<Form.Item name="contractUrl" label="签约合同">
|
||||||
|
<OssUpload bizType="CONTRACT" mediaType="FILE" accept="image/*,.pdf" />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{createStep === 2 && (
|
||||||
|
<>
|
||||||
|
<Form.Item name="bankAccountName" label="户主姓名" rules={[{ required: true, message: '请填写户主姓名' }]}>
|
||||||
|
<Input placeholder="银行卡实名姓名" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="bankAccountNo" label="银行卡号" rules={[{ required: true, message: '请填写银行卡号' }]}>
|
||||||
|
<Input placeholder="16~19位银行卡号" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="bankBranch" label="开户支行" rules={[{ required: true, message: '请填写开户支行' }]}>
|
||||||
|
<Input placeholder="例如:中国工商银行洛阳分行" />
|
||||||
|
</Form.Item>
|
||||||
|
<Alert
|
||||||
|
type="info"
|
||||||
|
showIcon
|
||||||
|
message="请确保银行卡信息准确,以免影响门店餐费结算。"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -97,6 +97,9 @@ export class OssAliyunProvider implements IOssProvider {
|
|||||||
const ossKey = buildOssObjectKey(this.uploadPrefix, input.bizType, input.fileName);
|
const ossKey = buildOssObjectKey(this.uploadPrefix, input.bizType, input.fileName);
|
||||||
await client.put(ossKey, input.buffer, {
|
await client.put(ossKey, input.buffer, {
|
||||||
mime: input.mimeType || 'application/octet-stream',
|
mime: input.mimeType || 'application/octet-stream',
|
||||||
|
headers: {
|
||||||
|
'Content-Disposition': 'inline',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
bucket: this.bucket,
|
bucket: this.bucket,
|
||||||
|
|||||||
@@ -136,6 +136,9 @@ export class AdminStoresService {
|
|||||||
if (!partner) throw new BadRequestException('开城合伙人不存在');
|
if (!partner) throw new BadRequestException('开城合伙人不存在');
|
||||||
const city = await this.prisma.commonCity.findUnique({ where: { id: BigInt(dto.cityId) } });
|
const city = await this.prisma.commonCity.findUnique({ where: { id: BigInt(dto.cityId) } });
|
||||||
if (!city) throw new BadRequestException('开城城市不存在');
|
if (!city) throw new BadRequestException('开城城市不存在');
|
||||||
|
if (city.partnerId && city.partnerId !== partner.id) {
|
||||||
|
throw new BadRequestException('开城城市与合伙人不匹配');
|
||||||
|
}
|
||||||
|
|
||||||
const store = await this.prisma.store.create({
|
const store = await this.prisma.store.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -149,10 +152,73 @@ export class AdminStoresService {
|
|||||||
district: dto.district ?? '',
|
district: dto.district ?? '',
|
||||||
address: dto.address,
|
address: dto.address,
|
||||||
intro: dto.intro ?? null,
|
intro: dto.intro ?? null,
|
||||||
|
bankAccountName: dto.bankAccountName ?? null,
|
||||||
|
bankAccountNo: dto.bankAccountNo ?? null,
|
||||||
|
bankBranch: dto.bankBranch ?? null,
|
||||||
|
openTime: '10:00',
|
||||||
|
closeTime: '22:00',
|
||||||
status: 'OPEN',
|
status: 'OPEN',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (dto.coverUrl) {
|
||||||
|
const cover = await this.prisma.commonResource.create({
|
||||||
|
data: {
|
||||||
|
ownerType: 'STORE',
|
||||||
|
ownerId: store.id,
|
||||||
|
bizType: 'COVER',
|
||||||
|
mediaType: 'IMAGE',
|
||||||
|
ossBucket: 'legacy',
|
||||||
|
ossKey: dto.coverUrl,
|
||||||
|
url: dto.coverUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await this.prisma.store.update({ where: { id: store.id }, data: { coverResourceId: cover.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
const envUrls = (dto.envPhotoUrls ?? []).filter(Boolean);
|
||||||
|
for (let i = 0; i < envUrls.length; i++) {
|
||||||
|
await this.prisma.commonResource.create({
|
||||||
|
data: {
|
||||||
|
ownerType: 'STORE',
|
||||||
|
ownerId: store.id,
|
||||||
|
bizType: 'ENV',
|
||||||
|
mediaType: 'IMAGE',
|
||||||
|
ossBucket: 'legacy',
|
||||||
|
ossKey: envUrls[i],
|
||||||
|
url: envUrls[i],
|
||||||
|
sortOrder: i,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.contractUrl) {
|
||||||
|
await this.prisma.commonResource.create({
|
||||||
|
data: {
|
||||||
|
ownerType: 'STORE',
|
||||||
|
ownerId: store.id,
|
||||||
|
bizType: 'CONTRACT',
|
||||||
|
mediaType: 'FILE',
|
||||||
|
ossBucket: 'legacy',
|
||||||
|
ossKey: dto.contractUrl,
|
||||||
|
url: dto.contractUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prisma.commonEvent.create({
|
||||||
|
data: {
|
||||||
|
eventType: 'STORE_AUDIT',
|
||||||
|
refType: 'STORE',
|
||||||
|
refId: store.id,
|
||||||
|
actorType: 'HQ',
|
||||||
|
status: 'APPROVED',
|
||||||
|
param1: 'NEW',
|
||||||
|
param1Desc: 'audit_type',
|
||||||
|
remark: 'HQ 后台新建',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await this.prisma.storeAccount.create({
|
await this.prisma.storeAccount.create({
|
||||||
data: {
|
data: {
|
||||||
storeId: store.id,
|
storeId: store.id,
|
||||||
@@ -161,7 +227,7 @@ export class AdminStoresService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return serializeBigInt(store);
|
return this.detailStore(store.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createStoreAccount(dto: CreateStoreAccountDto) {
|
async createStoreAccount(dto: CreateStoreAccountDto) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IsIn, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
|
import { IsArray, IsIn, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
export class UpdateStoreStatusDto {
|
export class UpdateStoreStatusDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@@ -58,6 +58,27 @@ export class CreateStoreDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
accountName?: string;
|
accountName?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
bankAccountName?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
bankAccountNo?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
bankBranch?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
@IsString({ each: true })
|
||||||
|
envPhotoUrls?: string[];
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
contractUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateStoreDto {
|
export class UpdateStoreDto {
|
||||||
|
|||||||
Reference in New Issue
Block a user